Skip to content

fix(desktop): order unnamed roster members by full canonical npub - #7503

Open
loganj wants to merge 2 commits into
fix/desktop-npub-identity-d2from
fix/desktop-roster-sort-full-npub
Open

fix(desktop): order unnamed roster members by full canonical npub#7503
loganj wants to merge 2 commits into
fix/desktop-npub-identity-d2from
fix/desktop-roster-sort-full-npub

Conversation

@loganj

@loganj loganj commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

  • Channel members appear in the Members sidebar. A member who has never set a display name is listed under an abbreviated form of their public key (npub), and the sidebar previously sorted those unnamed members by that short label. Short labels are not unique — different keys can share one — so the order of unnamed members could look arbitrary or unstable. Unnamed members now sort by their full public key, so the order is deterministic.
  • When two members display the same name, the previous tiebreak was membership order (who joined first), which is not visible to a reader and can shift as roster data loads in. The tiebreak is now the full public key, so identical display names always land in the same order.
  • Nothing gets noisier on screen: the full key is used only for sorting, and the sidebar still shows the compact abbreviated form. Priorities are unchanged — authored (custom) names still outrank fallback labels, and role/current-user grouping still applies.
  • Scope is the desktop app's Members sidebar and member management: the two existing sort comparators. Mobile and other lists in the app are untouched.

Related issue

Based on #7495 (introduced the abbreviated npub labels this follows up on). The original five presentation PRs remain independently reviewable. No closer duplicate found.

Testing

  • 6469 desktop unit tests, typecheck, and check pass.
  • The 3 existing consumer-seam E2E tests still pass; a new E2E test asserts the sidebar lists unnamed members in full-key order, with fixture members deliberately inserted in the opposite order so incoming membership order cannot mask the sort.
  • Negative check: reverting only this change makes the new ordering assertion fail, so it genuinely binds the new sort.
  • CI has not run on this PR yet.

Buzz provenance: channel 1f0e4a3d-7e01-4efe-bb16-843b357f85c9 / task 340c3de9b27dbedb8453c0c7652220f9080d30fcc70a7c4f6e27fdd4fa378056

@loganj
loganj marked this pull request as ready for review September 8, 2026 22:30
@loganj
loganj requested a review from a team as a code owner September 8, 2026 22:30

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:bot: Jude’s code review agent

Verdict: APPROVE
Reviewed: a1ffa774b291557c57860baecc05f2220b6dc8ae..07bcf2e4051335b4d119d76a29d2c1b6fec71193 (exact head 07bcf2e4051335b4d119d76a29d2c1b6fec71193)
Risk: medium — shared Desktop roster comparators change visible ordering across the Members sidebar and channel-management member lists, but do not alter identity storage, authorization, relay contracts, row identity, or interaction semantics.

Behavior/contracts traced: canonicalNpub normalization and invalid-key fallback; authored-name, unnamed-member, duplicate-name, current-user, and role precedence; all production consumers of compareMembersByRole, compareMembersForModal, compareMemberNames, and formatMemberName; virtualized row identity; the focused visible-order regression seam. The change matches the PR’s deterministic-ordering intent and the repository identity/display direction without expanding into relay, schema, persistence, or release scope.

Findings: no blocking or non-blocking code defect found. desktop/src/features/channels/lib/memberUtils.ts:28-79 keeps compact npubs presentation-only, orders unnamed members by full canonical npub, and deterministically breaks collation-equal surfaces by the full identity key. Existing current-user and role precedence remain ahead of that stage at memberUtils.ts:81-96; the Members sidebar preserves its coarser owner/admin/other ranking at desktop/src/features/channels/ui/MembersSidebar.tsx:108-127. Full pubkeys remain the stable rendered-row identity, so reordering does not retarget actions. The unit cases at memberUtils.test.mjs:37-121 cover opposite input orders, duplicate names, all role tiers, current-user precedence, and invalid keys; the visible test at desktop/tests/e2e/channels.spec.ts:4266-4315 binds the actual sidebar comparator and fails under the old compact-label ordering.

Author action: none.
Verification owner: reviewer/tooling for any optional native installed-app spot-check; CI/release gate for normal merge policy.

Validation at matching clean HEAD:

  • just desktop-ci — PASS at 07bcf2e4051335b4d119d76a29d2c1b6fec71193; clean before and after.
  • Independent full Desktop unit package — PASS, 6,469/6,469.
  • Independent focused Playwright visible-order journey — PASS, 1/1.
  • Independent mutation proof replacing the sidebar delegation with the old compact-label comparator — expected FAIL (v24Position=4, v5Position=3); restoration returned the tree clean.
  • Independent comparator mutation reverting compareMembersByRole to compact-label ordering — expected unit failures; restoration returned the tree clean.
  • git diff a1ffa774b291557c57860baecc05f2220b6dc8ae..HEAD --check — PASS.
  • Live required checks inspected at the exact head: Desktop core, all four smoke shards, integration shards, macOS/Windows builds, DCO, Semgrep, and zizmor green; no pending or failed check run.

Manual/native evidence: focused browser-rendered Members sidebar workflow exercised at the exact head. Native Tauri/VoiceOver observation was not run; no semantics, layout, or input code changed.

Residual risk: a real installed-app/native assistive-tech pass could still expose an environment-specific presentation issue, but this is a non-blocking reviewer-verification gap, not author rework. The changed comparator behavior is covered at both pure and rendered production seams with mutation evidence.

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: APPROVE

Reviewed: a1ffa774b291557c57860baecc05f2220b6dc8ae..07bcf2e4051335b4d119d76a29d2c1b6fec71193 (exact live head 07bcf2e4051335b4d119d76a29d2c1b6fec71193)

Risk: medium — user-visible ordering in shared roster comparators and a virtualized member list; no relay, persistence, IPC, identity-storage, or release contract changes.

Behavior/contracts traced: authored-name ordering, full canonical-npub fallback and tie-breaking, malformed-key fallback, current-user/role precedence, classified sidebar/archived lists, member management, stable row identity/actions, and the visible virtualized roster seam. Compact npub labels remain presentation-only. Canonically equivalent valid encodings compare equal, as they represent the same identity.

Findings: no blocking or non-blocking code defects. The implementation centralizes the name/key stage in desktop/src/features/channels/lib/memberUtils.ts:28-79; both production ordering paths reach it through compareMembersByRole or compareMemberNames (useClassifiedMembers.ts:52-82, MembersSidebar.tsx:108-127,188-194). Full pubkeys remain React/virtual-list keys (MembersSidebar.tsx:687-689,815-823), so reordering does not retarget member actions. Unit coverage exercises opposite input orders, compact/full disagreement, duplicate names, role tiers, current-user precedence, and invalid keys (memberUtils.test.mjs:36-121). The rendered E2E inserts the collision pair opposite the expected order and asserts their relative row order (channels.spec.ts:4266-4315).

Author action: none.

Verification owner: CI/release owns normal merge and artifact gates. An installed-app/native accessibility spot-check is optional and non-blocking because no semantics, layout, or input code changed.

Validation at matching exact head:

  • Two independent clean-checkout runs of the full Desktop unit package: pnpm test / pnpm --dir desktop test6,469 passed, 0 failed.
  • pnpm typecheck && pnpm checkpass.
  • Focused built smoke E2E for full-npub roster ordering — 1/1 pass.
  • Mutation proof at the production modal seam: restoring the old compact-label comparison made the focused E2E fail on all three attempts (v24Position=4, v5Position=3); restoring exact head passed.
  • Comparator mutation: restoring the old compareMembersByRole behavior failed the new full-npub/invalid-key unit assertions; restoring exact head passed.
  • git diff --checkpass; exact-head worktree clean.
  • Fresh CI inspection: Desktop core, four smoke shards, both integration shards, Windows/macOS builds, DCO, Semgrep, and zizmor are successful; PR reports CLEAN.

Manual/native evidence: no real Tauri/VoiceOver observation. Browser-rendered row order is directly proven by the focused E2E; no accessibility semantics changed.

Residual risk: native observation was not run. Given the bounded comparator-only production change, direct browser seam coverage, mutation evidence, full package tests, and platform builds, this does not imply author rework or block approval.

— :bot: Jude’s code review agent

@loganj
loganj force-pushed the fix/desktop-npub-identity-d2 branch from 8cce3c0 to 30cac57 Compare September 8, 2026 23:26
loganj and others added 2 commits September 8, 2026 19:26
The roster ordered members by the compact `npub1abcd…wxyz` display
label — a recognition aid that hides almost the entire key — so two
distinct identities sharing an npub head and checksum tail collapsed
into one roster position. Sort unnamed members by their full canonical
npub instead, and break every collation-equal name surface — duplicate
authored names, matching labels, invalid keys — by the full identity
key, so incoming membership-event order is never the tie policy. The
compact label stays display-only and unchanged.

- memberUtils: new shared `compareMemberNames` owns the name stage —
  surface (authored name, else full canonical npub), then full identity
  key for ties. `compareMembersByRole` delegates to it with its
  current-user and role precedence unchanged.
- MembersSidebar: the add-member modal comparator keeps its
  intentionally coarser owner/admin rank and current-user-first
  priority and delegates the same name stage.
- e2e: the virtualization spec's ordering comment follows the new rule
  (comment-only; the spec already resolves members from the rendered
  window).
- unit tests: five cases bind the production comparator — full-npub vs
  compact-label disagreement, duplicate-name identity-key tie-break,
  role precedence, current-user precedence, invalid-key determinism.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
The full-npub ordering change bound its five unit cases to
compareMembersByRole only. The visible members-sidebar roster sorts
through compareMembersForModal, whose delegation to the shared name
stage had no regression: reverting just that delegation back to
formatMemberName(...).localeCompare kept every existing test green.

Add one focused e2e case on the existing small-sidebar workflow
seams: the two unnamed fixtures from the memberUtils unit pair join
the three-member "random" roster — both visible in the initial
virtual window, no 500-member roster needed — arriving in the
opposite of the expected order, and the rendered roster must place
V24 before V5. The full canonical npubs disagree with the compact
labels for exactly this pair, so a display-label order fails the
assertion.

Falsified against ec7a9cf by reverting only the modal delegation
(byte-identical to parent a1ffa77): the new case failed at the
order assertion while both existing members-sidebar workflows stayed
green; restored production passes all three. Production files are
untouched and byte-identical to ec7a9cf.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj
loganj force-pushed the fix/desktop-roster-sort-full-npub branch from 07bcf2e to 4d2aff9 Compare September 8, 2026 23:26

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:bot: Jude’s code review agent

Verdict: APPROVE
Reviewed: 30cac57f71a374d1176667c5c7efcf4c76070e05..4d2aff9af4b17df989be9cf1b5c4e473adaf8ee5 (exact head 4d2aff9af4b17df989be9cf1b5c4e473adaf8ee5)
Risk: medium — deterministic roster ordering across shared member-list consumers and a visible sidebar seam.
Behavior/contracts traced: current-user/role precedence; authored-name versus full canonical-npub ordering; full-identity tie-breaks; classified member lists, channel management, and sidebar grouping; virtualized row identity/action targeting; relay/IPC/persistence boundary impact.

Findings: no unresolved author-actionable defect. Both assigned lanes are clear. The changed comparator preserves current-user and role precedence, sorts unnamed members by full canonical npub rather than ambiguous compact labels, and retains full-pubkey row keys so reordering cannot retarget member actions. The changed-head E2E now binds the actual rendered sidebar seam and reverses under mutation of production delegation.

Author action: none.

Verification owner: CI/release gate for the remaining nonterminal exact-head Desktop Core and Smoke shard 4 checks; reviewer/tooling for optional native observation. A later PR-caused gate failure or new head requires fresh review.

Validation: clean detached exact-head lanes passed git diff --check, full Desktop unit suite (6,470/6,470), just desktop-ci, and a focused built Playwright journey. Independent mutation of the production comparator delegation failed the intended rendered-order assertion and restoration returned a clean tree. At final refresh, Windows/macOS builds, smoke shards 1–3, relay-backed integration shards, DCO, Semgrep, and zizmor were green; Desktop Core and Smoke shard 4 were still running without failure.

Manual/native evidence: no fresh native Tauri/VoiceOver artifact; sorting changes no semantics, focus, input, labels, or layout.

Residual risk: exact-head CI completion remains externally owned. One product-lane local Playwright command selected zero tests due to a wrong testDir-relative path, but the systems lane’s focused built journey passed and the checked-in CI smoke journey is running; this is reviewer-command debt, not a defect.

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: APPROVE

Reviewed: 30cac57f71a374d1176667c5c7efcf4c76070e05..4d2aff9af4b17df989be9cf1b5c4e473adaf8ee5 (exact head 4d2aff9af4b17df989be9cf1b5c4e473adaf8ee5)

Risk: medium — user-visible ordering in shared Desktop roster comparators and a virtualized member list; no relay, IPC, persistence, authorization, identity-storage, lifecycle, schema, or release contract changes.

Behavior/contracts traced: authored-name/full-canonical-npub ordering and deterministic identity tie-break; malformed-key fallback; current-user and role precedence; classified people/bot/archive lists; Channel Management; the sidebar's coarser owner/admin/other grouping; stable virtualized-row identity/actions; and the rendered sidebar regression seam.

Findings: no blocking or non-blocking code defect found. desktop/src/features/channels/lib/memberUtils.ts:28-79 owns the name/full-canonical-npub stage and deterministic identity tie-break while current-user and role precedence remain ahead of it at memberUtils.ts:81-96. Production paths delegate through useClassifiedMembers.ts:52-82, ChannelManagementSheet.tsx:135-141, and MembersSidebar.tsx:108-127,188-194. Full pubkeys remain rendered/virtual-list keys at MembersSidebar.tsx:682-689,815-823, so reordering does not retarget actions. The changed-head E2E inserts the collision pair opposite expected order and asserts the actual visible sidebar order (desktop/tests/e2e/channels.spec.ts:4266-4315).

Author action: none.

Verification owner: CI owns the still-running exact-head Desktop Core gate. This is an external merge gate, not author rework. Reviewer/tooling owns any optional installed-native accessibility observation.

Validation at matching exact head:

  • Independent clean detached checkout: merge-base matched the reviewed base; git diff --check passed; tree remained clean.
  • Full Desktop unit package just desktop-test6,470/6,470 passed.
  • Independent just desktop-cipassed, including Desktop checks, typecheck/build/unit, and Tauri test coverage.
  • Focused built Playwright sidebar journey — 1/1 passed.
  • Mutation proof at the production compareMembersForModal delegation — all three attempts failed on the intended ordering assertion (v24Position=4, v5Position=3); restoring exact head returned the tree clean.
  • Live exact-head CI: all four smoke shards, both integration shards, macOS/Windows builds, DCO, Semgrep, and zizmor passed; Desktop Core remains in progress with no failed check.

Manual/native evidence: no installed Tauri/VoiceOver observation. No semantics, labels, focus, input, or layout code changed; browser-rendered row order is directly covered.

Residual risk: exact-head Desktop Core has not reached a terminal state. Normal branch protection must keep owning that gate. Native assistive-tech observation was not run, but the bounded comparator-only change, production-seam E2E, mutation evidence, full package validation, and platform builds make this non-blocking.

— :bot: Jude’s code review agent

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