fix(desktop): anchor active-turn badge to skew-corrected agent start#1068
Merged
Conversation
The badge elapsed counter anchored to Date.now() at event-processing time. Turns processed in one JS tick (buffer replay, batched syncAll) shared an anchor and ticked in lockstep, and a turn that began minutes ago rendered 0s on first observe. Maintain a per-agent clock offset — the running minimum of Date.now() - Date.parse(event.timestamp) — and derive each badge anchor at read time as startedAt + offset. Read-time derivation lets a later, tighter offset retroactively correct every live turn; the running minimum converges on true skew minus minimum network delay and is immune to per-event jitter, preserving the original clock-skew protection without trusting the raw agent-host clock. Distinct agent starts now yield distinct anchors (no lockstep); a long-running turn anchors into the past. Renames the ActiveTurnSummary.observedAt field to anchorAt to match the new read-time semantics, updating all three consumers. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Two review-driven follow-ups on the skew-corrected anchor fix. The design derives each badge anchor at read time so a later, tighter clock offset retroactively corrects every live turn — the load-bearing reason the anchor is not frozen at insert. No committed test asserted it: a regression that froze anchorAt at startTurn would have passed the whole suite. Add a test that goes live under a loose offset, tightens it via a delay-free liveness sample, and asserts the live turn's read-time anchor shifts earlier by exactly the tightening delta. Also scope the clockOffsetByAgent comment: the running minimum is only conservative (never over-reports) while true skew is constant or shrinking. Under growing skew it goes stale-too-small and elapsed can inflate — bounded and sub-second, but the prior comment overstated the guarantee. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96
pushed a commit
that referenced
this pull request
Jun 16, 2026
* origin/main: (50 commits) chore(release): release version 0.3.24 (#1074) feat(desktop): refine thread-unread badge to two-token form (#1069) fix(buzz): prevent reconnect storms from reaped ephemeral channels (#1071) fix(buzz-acp): trim oversized observer frames to fit instead of dropping (#1072) perf(ci): speed up PR CI wall clock and local dev builds (#1028) chore(deps): update react monorepo (#1048) Polish desktop visual details (#1067) ci: use running postgres for pgschema desired-state planning (#1070) fix(desktop): anchor active-turn badge to skew-corrected agent start (#1068) feat(desktop): add configurable transport reconnect hook (#1059) Add automatic database migrations (#988) Add composer spoiler formatting (#1055) feat(desktop): in-channel and in-thread unread indicators (#1008) perf(timeline): gate heavy message render behind useDeferredValue (#1022) Add animated profile avatars (#1031) Polish direct message and members modals (#1054) Polish huddles UI (#1041) Fix video review comments in threads (#1056) Polish message reaction tray (#1002) Refine app loading skeletons (#1001) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # desktop/src-tauri/Cargo.lock
wpfleger96
added a commit
that referenced
this pull request
Jun 16, 2026
…fleger/persona-instantiation * origin/wpfleger/persona-events: (50 commits) chore(release): release version 0.3.24 (#1074) feat(desktop): refine thread-unread badge to two-token form (#1069) fix(buzz): prevent reconnect storms from reaped ephemeral channels (#1071) fix(buzz-acp): trim oversized observer frames to fit instead of dropping (#1072) perf(ci): speed up PR CI wall clock and local dev builds (#1028) chore(deps): update react monorepo (#1048) Polish desktop visual details (#1067) ci: use running postgres for pgschema desired-state planning (#1070) fix(desktop): anchor active-turn badge to skew-corrected agent start (#1068) feat(desktop): add configurable transport reconnect hook (#1059) Add automatic database migrations (#988) Add composer spoiler formatting (#1055) feat(desktop): in-channel and in-thread unread indicators (#1008) perf(timeline): gate heavy message render behind useDeferredValue (#1022) Add animated profile avatars (#1031) Polish direct message and members modals (#1054) Polish huddles UI (#1041) Fix video review comments in threads (#1056) Polish message reaction tray (#1002) Refine app loading skeletons (#1001) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
tellaho
added a commit
that referenced
this pull request
Jun 17, 2026
* origin/main: (26 commits) fix(desktop): restore timeline zoom via rem tokens + chat-as-base type scale (#1052) fix(release): format changelog as linked markdown bullets (#1075) chore(release): release version 0.3.24 (#1074) feat(desktop): refine thread-unread badge to two-token form (#1069) fix(buzz): prevent reconnect storms from reaped ephemeral channels (#1071) fix(buzz-acp): trim oversized observer frames to fit instead of dropping (#1072) perf(ci): speed up PR CI wall clock and local dev builds (#1028) chore(deps): update react monorepo (#1048) Polish desktop visual details (#1067) ci: use running postgres for pgschema desired-state planning (#1070) fix(desktop): anchor active-turn badge to skew-corrected agent start (#1068) feat(desktop): add configurable transport reconnect hook (#1059) Add automatic database migrations (#988) Add composer spoiler formatting (#1055) feat(desktop): in-channel and in-thread unread indicators (#1008) perf(timeline): gate heavy message render behind useDeferredValue (#1022) Add animated profile avatars (#1031) Polish direct message and members modals (#1054) Polish huddles UI (#1041) Fix video review comments in threads (#1056) ... Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com> # Conflicts: # desktop/src/features/messages/lib/useRichTextEditor.ts # desktop/src/features/messages/ui/MessageComposer.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The active-turn badge elapsed counter anchored to
Date.now()at the moment the desktop processed the turn event. Two consequences:syncAll()) all received the sameDate.now()anchor, so their timers ticked in perfect lockstep — the user-reported "two timers at exactly the same number, in sync" symptom.0sthe instant the desktop first observed it.Why this approach
Switching the anchor to the raw agent-host
startedAtwas off the table — it reintroduces agent-host/desktop clock skew into the elapsed display, which the originalDate.now()anchor existed to prevent.Instead the store maintains a per-agent clock offset = running minimum of
Date.now() - Date.parse(event.timestamp), sampled on every fresh observer event. The badge anchor is derived at read time ingetActiveTurnsForAgentasstartedAt + offset(agent):The
ActiveTurnSummary.observedAtfield is renamed toanchorAtto match the new semantics; the three consumers (UserProfilePanelSections,UserProfilePopover,ManagedAgentRow) get the identical mechanical rename and nothing else.Scope
5 files, +263/-73. Logic + tests in
activeAgentTurnsStore.{ts,test.mjs}; pure field rename in the three consumers.