Repository navigation
Fix status pills not rendering on workspaces created after launch - #5660
obvioussean wants to merge 1 commit into
Conversation
|
@obvioussean is attempting to deploy a commit to the Manaflow Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Add a one-shot deferred snapshot refresh (.task) to TabItemView, running on the next main-actor tick after the view's .onReceive subscriptions are established. The sidebar observation publishers use .dropFirst() to skip the initial current-value replay from @published, making them change-only. This is correct and intentional — removing .dropFirst() would create a refresh storm in the extension sidebar (its publisher is rebuilt on each body evaluation, so subscription-time replay → state mutation → body re-eval → new subscription → replay → loop) and add ~7 un-debounced replays per row on the typing-latency-sensitive immediate publisher path. The bug: .onAppear fires synchronously during view insertion, before .onReceive subscriptions are live. Sidebar data set by socket commands (e.g. agent hook set_status) between .onAppear and subscription goes unobserved — the change-only publishers don't replay it, and the snapshot from .onAppear captured the pre-mutation state. Pre-existing workspaces aren't affected because their .onReceive subscriptions were established before any status was set. The .task modifier runs asynchronously after the full modifier chain is evaluated (including .onReceive subscription). It re-reads tab state at that point, catching any mutations that landed in the gap. The snapshot comparison (SidebarWorkspaceSnapshotRefreshPolicy.decision) no-ops when nothing has changed, so the cost for pre-existing workspaces is one cheap comparison per row appearance. Closes #5659 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Closing in favor of #5662 which fixes this at the publisher level with |
Status pills set on a workspace created after app launch are stored in the
Tabmodel but never render in the sidebar — while pre-existing workspaces render immediately.list-statusconfirms the data; the UI shows nothing.Why
.onAppearfires synchronously during view insertion, before.onReceivesubscriptions are live. Sidebar data set by socket commands (e.g. agent hookset_status) between.onAppearand subscription goes unobserved: the observation publishers use.dropFirst()to skip the current-value replay, making them change-only — so a mutation that lands before the subscriber connects isn't replayed, and the.onAppearsnapshot captured the pre-mutation (empty) state. Pre-existing workspaces work because their.onReceivesubscriptions were established before any status was set.What didn't work: removing
.dropFirst()globallyThe initial approach removed
.dropFirst()fromsidebarObservationSignaland the$panelschain, turning the publishers from change-only into replay-on-subscribe. This fixed the immediate bug but introduced three problems caught by adversarial review (GPT-5.5 + GPT-5.4, independently):extensionSidebarDebouncedObservationPublisheris a computed function that creates a newMergeManyper body evaluation. Subscription-time replay → handler increments@Statetoken → body re-eval → new subscription → replay → self-sustaining churn.makeWorkspaceSnapshot().publishCount == 0after subscribe + heartbeat mutation).What works: targeted deferred refresh
Add a one-shot
.tasktoTabItemViewthat callsrefreshWorkspaceSnapshot()— running asynchronously on the next main-actor tick after the view's modifier chain (including.onReceivesubscriptions) is fully established. It re-reads tab state at that point, catching any mutations that landed in the.onAppear-to-subscription gap.SidebarWorkspaceSnapshotRefreshPolicy.decisionno-ops when nothing has changed, so the cost for pre-existing workspaces is one cheap snapshot comparison per row appearance.The observation publishers keep their
.dropFirst()and change-only contract. No other consumers are affected.Validation
Couldn't build locally (SPM resolution failures in the worktree) — CI will validate. The change is +11 lines in one file (a
.taskmodifier onTabItemView).Closes #5659
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com