Skip to content

P1032: repaint channel members only for changed pins - #1369

Merged
trissim merged 2 commits into
mainfrom
work/toad-sidebar--P1032
Oct 30, 2026
Merged

trissim merged 2 commits into
mainfrom
work/toad-sidebar--P1032

Conversation

@trissim

@trissim trissim commented Oct 30, 2026

Copy link
Copy Markdown

Summary

1 commit, 2 files, +4/-4. Channel-change repaint requests go from every member to only the channel's pin delta. The blanket group sync spelling goes 5 -> 4 in sidebar_tree.py.

channel unread changed
repaint every thread
ask every open tab whether it changed
nothing changed

Channel presentation owns the pin delta

Before: ChannelGroup.present painted the header and discarded the difference between its previous and new channel. ChangedChannels.apply_comms then marked every member changed. Every resulting ThreadStatusRow.show calls SessionNavigation.thread_row before its existing equality guard; that method walks all open session entries.

After: ChannelGroup.present(channel) -> frozenset[str] retains all existing header work and returns previous.pinned_members ^ channel.pinned_members. ChangedChannels passes that result directly into group.sync_members(...).

Class structure is unchanged: ChannelGroup(SidebarGroup[ThreadRow]), SidebarGroup(KeyedRows[str], VerticalGroup), and the existing SidebarChange family remain the owners. No cache, throttle, shadow state, wrapper or equality workaround was added. One blanket repaint route (2 statements) is replaced with the existing owner's result (1 statement).

Work removed and preservation scope

Let M be existing attached member rows in one changed group, T open session entries, and D the pin symmetric difference among those rows. With stable membership:

Channel update Before row-show calls / tab projections After
Unread/activity/label only; unchanged pins M / M*T 0 / 0
Pin changes M / M*T D / D*T

These are static work counts, not elapsed timings or measured p99.9. Membership reconciliation still traverses model keys; no claim that the entire channel update is constant time.

  • KeyedRows.reconcile_keys creates and paints new/transferred or unattached rows independently of the changed set, removes retired keys and reorders from current keys. That algorithm is unchanged.
  • RowReconciliation.finish keeps its full-member synchronization for outstanding transfers.
  • AllSidebarRows keeps full catch-up for hidden/reopened/rebound views. New groups still paint through on_mount.
  • ChangedThreads and app ThreadRowStateChanged still repaint actual thread changes.
  • Search filtering/reconciliation and selection/reader handling are untouched.
  • Only the per-member pin input comes from the channel; thread content comes from shown.threads.rows and app session state.

Source evidence

Before inventory: NRA parse_python_module_roots + ModuleSyntaxIndex, Toad production source plus Core ui_model, 318 parsed modules, completed RC0. It records class bases, imports, relevant owner bodies and attribute reads/writes; all matching present, sync_members, show_member, reconciliation, pin and thread-row consumers were inspected. This is syntax evidence, not a dynamic binding/MRO proof; unrelated present names remain unmatched rather than being declared sidebar callers. No parse error was printed.

Owner/consumer sites before this patch:

  • comms_sidebar.py:100,132,146,270: channel presentation, pin projection, member sync, thread rendering.
  • sidebar_tree.py:180,376,434,457,483,722: reconciliation, catch-up, channel changes, thread changes, search.
  • session_navigation.py:178: per-row session traversal.
  • Core ui_model/sidebar.py:81,189: immutable channel row and separately flushed channel/thread models.

Required check:

git grep -h -c -F 'group.sync_members(frozenset(group.member_keys))' -- src/toad/widgets/sidebar_tree.py
# before: 5; after: 4

No pytest, synthetic app, fixture, full suite or local NRA gate was run. The integration owner still owes installation and the affected real-application journey/total-frame p99.9 measurement; this PR does not claim those results.

Assumed:

@trissim
trissim marked this pull request as ready for review October 30, 2026 15:24
Copilot AI balanced review requested due to automatic review settings October 30, 2026 15:24
@trissim
trissim enabled auto-merge October 30, 2026 15:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@trissim
trissim merged commit b4f29e0 into main Oct 30, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants