Repository navigation
C11-243: record when the operator last looked at each tab - #461
Conversation
Drawbridge deep review — PR #461: record when the operator last looked at each tabThank you for this — it's a careful, well-shaped contribution and a genuine pleasure to review. The "being seen" definition is written down once (top of SummaryAdds a per-tab AlignmentSquarely in scope. This is an agent-facing telemetry primitive on addressable surfaces — one of the "clearly wanted" categories in I re-verified the load-bearing claims against the trusted checkout:
FindingsNo blockers. 1. (minor, accuracy) A transient auxiliary key window stamps the visible tab as "stopped being seen." 2. (minor, robustness) 3. (nit) 4. (nit) Test file lives in On the tests generally: the pure-state-machine cases are exactly right, and RecommendationMerge after fixes — where "fixes" is really just optional polish (findings 1–4 are all minor/nit and could land here or in a follow-up) plus two operational checks. Before merge, please confirm: CI is green on the final head (the PR notes the positive stamping path wasn't exercised live, which is a reasonable limitation given it needs a foregrounded app and a human-style focus change — the state-machine tests plus code reading cover it); and, since the skill source was edited, that Nice work — thank you for the contribution. 🌉 Drawbridge deep review (lane 3) — an automated pass; a maintainer has been notified and makes the final call. 🌉 Drawbridge deep review (lane 3) — an automated pass; a maintainer has been notified and makes the final call. |
Per-tab last-seen clock. A tab is being seen while it is the selected tab of the focused pane, in the selected workspace of the key main window, with c11 frontmost and the screen unlocked; lastSeenAt is the moment it last stopped being seen. Derived from live focus state and refreshed on tab/pane/workspace selection, key window, app activation and screen lock/sleep, so background programmatic focus changes stamp nothing. Persisted as last_seen_at on the session panel snapshot, exposed as last_seen_at/being_seen in surface.list and system.tree (c11 tree --json), and carried on SurfaceActivityDetailsSnapshot for the tab sheet. Skill and api reference updated. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…n close, injectable tracker) - Interrupts are a Set of independent reasons (locked, screensaver, displays asleep, system asleep, session inactive); one clearing never lifts another. - Seen also requires the key window on the active Space and not occluded. - Panel stamps are dropped on tab close, pane close and workspace teardown (not on detach/move, where the id survives). - One static ISO8601 formatter for socket output; set fields directly. - Refresh when a main window registers or becomes the active window. - Restore last_seen_at inside applySessionPanelMetadata on the created id. - Tracker takes an injectable seen provider and clock; tests cover refresh, lock-then-wake staying interrupted, close-then-forget. - SurfaceActivityDetailsSnapshot gains isBeingSeen; docs updated. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…amp for the sheet - App activation clears screensaver and sleep reasons (c11 cannot be frontmost while they hold) and re-derives .locked from the window server. - Distributed lock/screensaver observers use .deliverImmediately; screensaver willstop also clears. - Closing a window forgets its panels' stamps (workspace moves re-home first). - SurfaceActivityDetailsSnapshot.lastSeenAt is the raw stored stamp; pair with isBeingSeen to render "now". Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
9af07c4 to
ffc5bd2
Compare
…e default clocks[seen] is the stored last-seen stamp from the tracker; while the operator is looking at the tab it reads "now" (host text) instead of an age. Default clock order is active,seen,launched again. Self-contained so it can be cherry-picked onto #463 once #461 lands. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Live model detection for agent tabs Read the model each harness is actually using from the session file c11 already locates for resume (Claude Code, Codex, pi, omp: incremental JSONL tail; Grok: summary.json; opencode: session.model in its SQLite), off-main on the AgentDetector 10 s sweep, and publish the raw id as model_detected at the derived tier. Declared/explicit model and launch stamps still win; detection fills in. Kimi and Copilot report unsupported instead of guessing. Friendly names (Opus 5.5, gpt-5.5) are shared by the sidebar chip and the tab sheet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Tab sheet signals: agent last-event Active, turn/tools/tokens/touched clocks, per-type Active - Agent tabs: Active is the last assistant message or tool result, read by the same transcript tail as model detection (Claude Code, Codex, pi, omp; opencode and Grok from their rows). Also turn duration, tool calls and tokens this turn. - Plain terminals: Active is the later of scrollback growth while visible (GHOSTTY_ACTION_SCROLLBAR, re-baselined on resize and visibility changes, decreases ignored) and the last command start/finish. Markdown: last content change (mtime at load). Browser: last load, skipping the first after restore or hibernate. Operator input is a separate 'touched' clock (plain Date stamp in keyDown and the web view). turn/tools/tokens/touched are opt-in via the clock order setting; the default is active,seen,launched. - Model precedence: agent-declared > detected > launch stamp. Launch stamps (launch-agent, A button, blueprints) are now recorded at the heuristic tier. - Bonsplit: additive clockTexts for non-date clocks (submodule branch tab-agent-signals). - debug.tab_sheet.detail socket method returns a surface's sheet detail. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Synthesized keys (socket send) do not stamp the touched clock Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Address review: wrapper no longer declares the launch model, Codex tokens dedupe, per-type Active fall-through, touched from text box and clicks - Resources/bin/claude stops forwarding CMUX_AGENT_MODEL as a declaration; the launch stamp (heuristic tier) already records it, so a later /model shows. Snapshots from before the tiering restore launch-stamped models at the launch tier (marker c11.model_tiering distinguishes newer snapshots). - Codex repeated token_count lines collapse by total_token_usage. - Active falls through to scrollback growth/command edges when an agent has no transcript event; legacy activity (which included keystrokes) is gone. Default clock order is active,launched again. - touched also stamps from the text box (keys and typing) and terminal clicks. - Scrollback tracker: settle window instead of skipping the first event after a reveal/resize; alt-screen exit no longer stamps the restored history. - Parsing: timestamps and roles come from parsed fields; a very long Codex turn finds its turn_context by backward substring search; a placeholder ref resets the previous session's state; Grok summary is a plain read. - Clock text follows the app language; panelShellEdgeAt is pruned and moved with surfaces; chip model source matches what display_label shows; privacy contract lists exactly what is retained; localization terms revised. - Bonsplit: tolerant decode of clocks/clockTexts (c52fc58). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Tab sheet: wire the Seen clock and restore active,seen,launched as the default clocks[seen] is the stored last-seen stamp from the tracker; while the operator is looking at the tab it reads "now" (host text) instead of an age. Default clock order is active,seen,launched again. Self-contained so it can be cherry-picked onto #463 once #461 lands. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Atin <benevolent.futures@gmail.com> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
What
Per-tab
lastSeenAt: when the operator last looked at each terminal, browser or markdown tab. Feeds the "Seen" clock in the tab sheet (sheet UI is out of scope here).Semantics (also documented in
Sources/SurfaceSeenClock.swift)A tab is being seen while it is the selected tab of the focused pane, in the selected workspace of the key main window, with c11 frontmost and the screen unlocked or awake.
lastSeenAtis the moment it last stopped being seen; while being seen it reports now; never seen isnull."Being seen" is derived from live focus state and recomputed on: tab/pane selection (
applyTabSelection), workspace switch (TabManager.selectedTabId), window key/resign key, app activate/deactivate, and screen lock, sleep and session-resign. Because it is derived from what is on screen, a programmatic or socket focus change while c11 is in the background stamps nothing. A socket command that changes the visible tab while the operator is looking does stamp the tab that left the screen (they did stop seeing it).Where it lives
SurfaceSeenClock(pure state machine) +SurfaceSeenTracker.sharedinSources/SurfaceSeenClock.swift. Read withSurfaceSeenTracker.shared.lastSeenAt(panelId:).SurfaceActivityDetailsSnapshot.lastSeenAt, besidecreatedAtandlastActivityAt.last_seen_atonSessionPanelSnapshot; restored increatePanel(from:). Rides the existing snapshot cadence, no extra writes.last_seen_at(ISO-8601 ornull) andbeing_seenon every surface insurface.listandsystem.tree, soc11 tree --jsoncarries them.skills/c11/SKILL.mdandreferences/api.mdupdated; installed copy synced.Performance
Refresh runs only from selection/focus/app events (never keystroke, hit-test or
forceRefresh). Steady state is a few property reads and an equality check; aDateis allocated only when the seen-panel changes.Validation
SurfaceSeenClockTests(9 tests, c11LogicTests) pass: transitions, no restamp, seed vs live, snapshot round-trip incl. legacy decode.tab-last-seen, QA-launched, backgrounded):c11 tree --jsonshowslast_seen_at: null,being_seen: false, and creating/focusing surfaces over the socket while c11 is inactive stamps nothing.🤖 Generated with Claude Code