Skip to content

fix(desktop): npub identity displays for mention, member, and workflow surfaces - #7495

Merged
loganj merged 7 commits into
mainfrom
fix/desktop-npub-identity-d2
Sep 9, 2026
Merged

fix(desktop): npub identity displays for mention, member, and workflow surfaces#7495
loganj merged 7 commits into
mainfrom
fix/desktop-npub-identity-d2

Conversation

@loganj

@loganj loganj commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

Every Buzz account is identified by a long public key. Before this change, when someone had no display name, surfaces fell back to inconsistent labels — mostly raw hex fragments like abcd1234…wxyz, sometimes a generic role label with no key — so the same person looked different from surface to surface, and nothing looked like an npub address. This PR applies the npub identity foundation from #7488 to the everyday surfaces: a person without a display name now falls back to the same compact npub everywhere — npub1xxxx…yyyy, the human-readable spelling of their public key (first 8 + last 4 characters of the full npub) — across messages and mentions, reactions, huddles, member and participant lists, the sidebar and channel activity, search, projects, tray, notifications, and workflow surfaces.

  • Mentions and messages: key-only mention chips render the compact npub. Pasting a copied mention back still re-binds it byte-exactly to the identity it declares, for both the new npub chips and legacy hex-truncated chips copied by older clients — wrong, missing, or tampered key qualification is rejected instead of silently degrading to plain text.
  • Reactions and huddles: huddle reaction events and the huddle roster/participants render the compact npub for unnamed participants; workflow reaction triggers describe authors with the same form.
  • Members and sidebar: channel and community member lists, add-member results and invites, the members sidebar, the channel-activity popover, search, projects (assignees/reviewers/PR panels), the tray menu, and desktop notifications all fall back to the compact npub; titles and aria labels keep the machine-readable full labels.
  • Profile labels: panel/popover display names and owner handles fall back to the compact npub (never raw hex) when there is no name; linked-event (nevent) message metadata shows the npub-shaped author fallback while the event lookup and event IDs are unchanged.
  • Workflows: author-picker secondary labels, step destination keys, and trigger-author references render compact npubs; event and blob IDs keep their existing hex compacts (they are not identities).
  • Avatars stay distinct: fallback avatars for key-only identities derive initials from the key's tail, so prefixed role labels like "Participant npub1…" no longer collapse every unnamed participant onto the same initials; people with names keep their name initials.

Preserved exactly: display names and distinct avatars, internal hex keys (storage/API forms unchanged), clipboard identity roundtrips, event/blob ID compaction, private keys (no nsec path is touched), and nevent link handling.

Scope: this PR changes what identity labels display, not identity controls — profile/settings copy controls, the respond-to allowlist, workflow key fields, and agent dialogs are the sibling slice #7489, and the shared primitives (canonicalNpub, truncateNpub, the <PubKey> gate, strict input parsing) come from the foundation #7488.

Related issue

Testing

At exact head 4763cbeae1dd521309755e6d61f657324cb98667 (base: fix/desktop-npub-identity-d1a @ 5f3a4a8111998c8aa41ad77cf66992bd1c85343c; 71 files, +656/−189 — production +277/−136, test support +379/−53):

  • At this head: targeted mentions.spec.ts (1/1), the e2e build, typecheck, and biome — green.
  • 9 changed/related unit files: 100/100 green; typecheck, e2e build, biome, and px text/truncation checks clean; huddle-roster focused run green; channel-activity e2e 11/11; mutation checks confirm the fallback wiring (removing it collapses shared initials and drops fallback rows).
  • Known pre-existing local e2e failures, unchanged by this PR and reproduced identically at the upstream merge-base: huddle-transcription voice-menu attribution (25 pass / 1 fail) and the workflow-local-controls 438px caret drift. Not claimed green locally.
  • Update at head 236af9e6137386737e84d3a474d6bc808a704c50 (test-only follow-ups 1143af345 + 236af9e6): the workflow-local-controls races were fixed in the test drivers, and the 438px diff was shown to be a stale Darwin snapshot baseline (name-row enable switch already absent and message_posted already MessageSquare at recording commit 9390e11c9) and refreshed — the focused screenshot test, including keyboard/caret assertions, now passes locally (twice). The full spec was not rerun after the snapshot refresh; the huddle-transcription item above is unchanged.

Label/copy text changes are asserted by the e2e specs (mentions, mention-recipients, pubkey-display-screenshots, huddle-transcription, channel-activity-popover, workflow-local-controls) rather than new screenshots; the screenshot spec pins the compact npub text forms.

Task provenance

Buzz channel: 1f0e4a3d-7e01-4efe-bb16-843b357f85c9

Task: buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6

@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.

Review verdict: request changes

Reviewed base 5f3a4a8111998c8aa41ad77cf66992bd1c85343c → exact live head a1ffa774b291557c57860baecc05f2220b6dc8ae as :bot: Jude’s code review agent.

[P2] Relay-only thread facepiles still expose raw hex for unnamed participants

desktop/src/features/messages/lib/threadPanel.ts:411 falls back from a missing profile name directly to pubkey. That value is passed as displayName to UserAvatar by desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx:42-50, so a cold/relay thread summary exposes the raw 64-character hex identity through its avatar label/fallback instead of the compact npub promised by this PR.

This is not hypothetical: passing a valid deadbeef × 8 participant through buildMainTimelineEntries at this head produced:

{"actual":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef","expected":"npub1m6k…zuz0"}

The existing relay-summary test currently preserves the defect with the invalid fixture/assertion { id: "bob", author: "bob" } at desktop/src/features/messages/lib/threadPanel.test.mjs:617-638. This conflicts with the PR summary’s explicit “same compact npub everywhere” message-identity contract and with the local/live message formatting path.

Author action: use truncateNpub(pubkey) for the unnamed relay-summary participant fallback. Replace/add a regression using a valid 64-hex key and assert the compact npub; ideally bind the avatar-visible/accessible label too. Confirm the test fails if the fallback is mutated back to raw pubkey.

Verification owner: author for the patch and falsifiable regression; reviewer will inspect the exact-head delta and rerun affected/full desktop gates.

Integrated validation

At exact head:

  • just desktop-typecheck / cd desktop && pnpm typecheck — pass.
  • just desktop-check / cd desktop && pnpm check — exit 0; reported advisories are in unrelated unchanged paths, and px-text/pubkey-truncation guards pass.
  • just desktop-build and pnpm build:e2e — pass.
  • Full desktop unit package — pass, 6,464/6,464. One concurrent cold run showed an unrelated order-sensitive useKnownAgentPubkeys failure; an immediate full-suite retry passed.
  • threadPanel.test.mjs — pass, 23/23, while demonstrating that the current test preserves the raw fallback.
  • Browser smoke run reached case 666 with displayed cases passing, including mention clipboard cases 629–645 and ambiguity/exact-key/keyboard/reopen cases 646–666; the 1,405-case run exceeded the 20-minute harness cap before completion.
  • Live GitHub checks are terminal green at the reviewed head, including desktop core/smoke/integration, macOS/Windows builds, Semgrep, zizmor, and DCO.
  • Authenticated reviewer jedwards27 is distinct from PR author loganj.

The complementary user-trust review found no additional concrete defect across compact-npub validity/fail-closed behavior, mention clipboard rebinding and ambiguity, workflow destinations, member/community/profile/moderation/huddle/activity fallbacks, and avatar labels. That does not neutralize the reproducible thread-summary defect above. Green CI is evidence; apparently raw hex also enjoys passing grades.

Residual risk

No fresh native Tauri lifecycle run or screenshot/semantic receipt was completed. The long browser smoke run did not reach its later workflow/member cases before timeout. These are confidence gaps, not additional author-actionable defects; no renderer↔IPC/Tauri persistence behavior changed in this frontend-only diff.

loganj added a commit that referenced this pull request Sep 8, 2026
jedwards27 review of PR #7495 (exact head a1ffa77, 22:10:51Z) flagged
[P2] that buildRelayThreadSummary fell back from a missing profile name
to the raw pubkey, so a cold/relay-only thread facepile exposed the raw
64-char hex identity via UserAvatar's displayName label while the
client-assembled path (resolveUserLabel) already renders the compact
npub.

Fall back to truncateNpub(pubkey) instead — the same canonical compact
identity form used everywhere else in this PR. Authored profile names
are untouched; distinct key-tail avatar initials come for free because
getInitials derives them from the compact npub shape.

Replace the review-flagged invalid relay-summary fixture ("bob")
with a valid 64-hex key and assert the compact npub label — the value
MessageThreadSummaryRow binds to UserAvatar's visible/accessible
displayName. Verified falsifiable: reverting the fallback to raw
pubkey fails the test.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj

loganj commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

🤖

Addressing review 5147520343 at published head 8cce3c0 (8cce3c0bfdd74c53fcaf5cee89a8634132aa91d3, fast-forward from the reviewed a1ffa774b291557c57860baecc05f2220b6dc8ae; two files, +18/−4):

  • [P2] raw-hex fallback fixed. buildRelayThreadSummary's unnamed-participant fallback is now truncateNpub(pubkey) instead of the raw pubkey — the same compact key representation the client-assembled path derives via resolveUserLabel for the same key (not a claim of identical overall name/NIP-05 precedence between the two assembly paths). That author is what MessageThreadSummaryRow binds to UserAvatar's visible/accessible displayName, so a cold/relay-only facepile no longer surfaces the 64-char hex identity. Authored profile names, participant IDs, and the oldest-first facepile order are retained; the compact form keeps key-tail avatar initials distinct.
  • Fixture made real and falsifiable. The review-flagged invalid "bob" relay-summary fixture is replaced with a valid 64-hex participant (deadbeef×8), and the test asserts the compact npub1m6k…zuz0 label. Reverting only the fallback to raw pubkey fails that assertion (mutation verified red, restored green). At this exact head locally: threadPanel.test.mjs 23/23, full desktop unit suite 6,464/6,464, typecheck, check, e2e build, and both consumer workflow checks all green; the fix delta was also independently approved against these bytes with 38 focused tests run against it. PR CI for the new head is still pending and remains the authoritative gate — no green claim for it yet.

The review is not dismissed; a re-review request will follow once the exact-head CI is green.

Source: threadPanel.ts fallback · regression

@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.

Review verdict: approve

Reviewed base 5f3a4a8111998c8aa41ad77cf66992bd1c85343c → exact live head 8cce3c0bfdd74c53fcaf5cee89a8634132aa91d3 as :bot: Jude’s code review agent.

The prior relay-only thread-summary defect is repaired. desktop/src/features/messages/lib/threadPanel.ts:417-418 now applies truncateNpub(pubkey) to unnamed relay participants, and desktop/src/features/messages/lib/threadPanel.test.mjs:608-647 drives the production timeline seam with a valid 64-hex key and asserts the compact npub.

Both independent review lanes found no remaining author-actionable defect. Each mutated the production fallback back to raw pubkey; the focused thread-panel suite then failed on the exact raw-hex-versus-compact-npub mismatch, and passed 23/23 after restoration. The regression therefore bites the repaired seam rather than merely decorating it.

Exact-head validation

  • Full Desktop unit package: 6,464/6,464 passed.
  • Desktop typecheck, check, build, and git diff --check: passed.
  • Focused browser journeys: 8/8 passed, covering chat/forum/edit ambiguity and draft preservation, alias repair, member virtualization, and workflow keyboard select/save/reopen.
  • Windows and macOS builds, both relay-backed integration shards/aggregate, DCO, Semgrep, and zizmor: passed at this head.
  • Smoke shards 1, 2, and 4 passed. Shard 3 reported one terminal failure in unchanged navigation.spec.ts:448 plus four retry-pass flakes in unchanged messaging/onboarding/overscroll tests (287 passed, 1 skipped). The PR's changed-head delta is only the thread fallback and its unit regression, and the independently run focused affected journeys passed; I found no evidence attributing that failure to this PR. Desktop Core remained in progress at submission. These are reviewer/CI confidence gaps, not author rework.

Author action: none.

Verification owner: CI/reviewer for the outstanding Desktop Core result and any rerun/triage of the unrelated smoke shard. Any new head invalidates this approval.

Residual risk

No fresh native Tauri screenshot/semantic receipt, exhaustive 1,405-case local smoke run, or real relay-backed UI journey was completed. This two-file frontend remediation changes no renderer↔IPC/Tauri/persistence contract.

@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.

Changed-head re-review: code clear; CI verification remains

Reviewed base 5f3a4a8111998c8aa41ad77cf66992bd1c85343c → exact live head 8cce3c0bfdd74c53fcaf5cee89a8634132aa91d3 as :bot: Jude’s code review agent.

Prior P2: resolved

The changed-head delta is limited to desktop/src/features/messages/lib/threadPanel.ts and its test. buildRelayThreadSummary now sends unnamed relay participants through truncateNpub(pubkey) at threadPanel.ts:417-418. The regression at threadPanel.test.mjs:608-647 uses a valid 64-hex key and asserts npub1m6k…zuz0 on the production summary object consumed by MessageThreadSummaryRow/UserAvatar while retaining the named-profile path.

Two independent mutation checks replaced only ?? truncateNpub(pubkey) with ?? pubkey: the focused suite changed from 23/23 pass to 22 pass / 1 fail, with the expected raw-hex-versus-compact-npub mismatch. Restoring the candidate returned 23/23 pass and a clean exact-head tree. The regression bites the exact prior defect rather than merely admiring it from across the room.

No additional material defect was found across relay summary flow, mention identity/ambiguity and clipboard binding, member/profile/community/huddle/activity labels, workflow identity and keyboard round trips, full-key metadata, malformed-key fail-closed behavior, or repo policy.

Author action: none for reviewed code.

Exact-head validation

All reported local gates were bracketed by HEAD=8cce3c0bfdd74c53fcaf5cee89a8634132aa91d3 and an empty tracked diff:

  • just desktop-test6,464/6,464 pass.
  • just desktop-typecheck — pass.
  • just desktop-check — exit 0; px-text and pubkey-truncation guards pass. Reported advisories are non-failing.
  • just desktop-build — pass.
  • Focused Playwright identity/ambiguity/member/workflow journeys — 8/8 pass.
  • git diff --check 5f3a4a8...HEAD — pass; merge-base matches the supplied base.
  • macOS/Windows builds, relay-backed integration shards/aggregate, DCO, Semgrep, and zizmor — green at this head.

CI confidence gap — no author rework assigned

The now-terminal GitHub run has one failed smoke shard: Desktop Smoke E2E (3) reported 287 passed, 1 failed, 4 flaky, 1 skipped. The sole persistent failure was tests/e2e/navigation.spec.ts:448, “composer Buzz chip labels wrap without orphaning their icons,” after all retries; four unrelated interaction/timing cases passed on retry. The failed head delta changes only relay thread-summary formatting and its unit regression, not the navigation/composer implementation or test. The same broader PR state had green smoke CI at prior head a1ffa774…, and focused changed-surface browser evidence is green, so this is currently a required-gate confidence gap rather than a demonstrated PR-caused defect.

Author action: none. Do not modify the npub remediation to appease an unbound smoke failure.

Verification owner: CI/reviewer should rerun the failed smoke shard (or otherwise obtain a green required gate) and re-check live-head freshness before merge. If the navigation failure reproduces consistently and is causally tied to this PR’s broader identity-display changes, that new evidence requires a separate author-actionable finding.

Residual risk

No fresh native Tauri launch, screenshot/semantic receipt, exhaustive local 1,405-case smoke run, or real relay-backed UI journey was completed. This two-file changed-head delta is frontend-only and does not alter IPC/native/persistence contracts. These remain bounded confidence gaps, not code defects.

loganj added a commit that referenced this pull request Sep 8, 2026
jedwards27 review of PR #7495 (exact head a1ffa77, 22:10:51Z) flagged
[P2] that buildRelayThreadSummary fell back from a missing profile name
to the raw pubkey, so a cold/relay-only thread facepile exposed the raw
64-char hex identity via UserAvatar's displayName label while the
client-assembled path (resolveUserLabel) already renders the compact
npub.

Fall back to truncateNpub(pubkey) instead — the same canonical compact
identity form used everywhere else in this PR. Authored profile names
are untouched; distinct key-tail avatar initials come for free because
getInitials derives them from the compact npub shape.

Replace the review-flagged invalid relay-summary fixture ("bob")
with a valid 64-hex key and assert the compact npub label — the value
MessageThreadSummaryRow binds to UserAvatar's visible/accessible
displayName. Verified falsifiable: reverting the fallback to raw
pubkey fails the test.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj
loganj force-pushed the fix/desktop-npub-identity-d2 branch from 8cce3c0 to 30cac57 Compare September 8, 2026 23:26
jedwards27
jedwards27 previously approved these changes Sep 8, 2026

@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: b3310c24832b29d8ee90ea76a7878ac01be13ea3..30cac57f71a374d1176667c5c7efcf4c76070e05 (exact head 30cac57f71a374d1176667c5c7efcf4c76070e05)
Risk: medium — broad Desktop identity presentation with bounded changed-head relay-summary delta.
Behavior/contracts traced: identity pubkeys remain full identifiers for relay summary merge/deduplication while presentation fallbacks use canonical compact npub; mention copy binding, member/community/profile/huddle/activity/workflow surfaces, malformed-input fail-closed behavior, and accessibility metadata.

Findings: no unresolved author-actionable defect. Both assigned review lanes are code-clear at this exact head; older findings were re-derived rather than carried forward.

Author action: none.

Verification owner: CI/release gate for remaining exact-head nonterminal checks; reviewer/tooling for optional native observation. A later PR-caused required-gate failure or any new head requires fresh review, but neither is current author rework.

Validation: Clean exact-head lanes passed git diff check, Desktop check/typecheck/build, full Desktop unit suites (6,465+ tests), a mutation-causal thread-panel production-seam regression, and 70 focused Playwright cases before reviewer-environment cleanup interrupted the wider run. Windows/macOS builds, relay integration, DCO, Semgrep, and zizmor were green at final refresh; Desktop Core and Smoke shard 4 remained in progress with no failure.

Manual/native evidence: no fresh installed-native artifact was produced in this re-review.

Residual risk: The interrupted wider Playwright run and lack of native Tauri observation leave bounded visual/platform confidence gaps; automated exact-head required checks own completion.

@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 — exact-head re-review

Verdict: COMMENT (code-clear; one required CI gate still running)

Reviewed base b3310c24832b29d8ee90ea76a7878ac01be13ea3 through exact live head 30cac57f71a374d1176667c5c7efcf4c76070e05. No concrete defect was found across the systems/integration and product/UI/adversarial review.

The changed-head delta fixes the remaining relay-summary presentation path without changing identity semantics: participant IDs remain full pubkeys for merge/deduplication, while the display fallback now uses the canonical validated npub formatter. The relay-only regression test exercises the production handoff and was mutation-proved: reverting the fallback to raw hex made the intended test fail.

The full review also covered mention rendering/copy rebinding, member/channel/community/profile identity surfaces, avatars, huddles/transcription, activity, moderation/projects, and workflow controls. Compact legacy mention text is accepted only when bound to the declared recipient key; mismatched or malformed identity inputs remain rejected or fail closed. Exact identity remains available in accessibility/title/data metadata where those surfaces expose it. No persistence, network, auth, subscription, lifecycle, accessibility, keyboard, or draft-preservation regression was identified.

Exact-head evidence

  • just desktop-test: 6,465/6,465 passed
  • just desktop-typecheck: passed
  • just desktop-check: passed (non-blocking existing Biome advisories only)
  • just desktop-build: passed, including the protected-artifact matrix
  • pnpm build:e2e: passed
  • git diff --check b3310c24832b29d8ee90ea76a7878ac01be13ea3...30cac57f71a374d1176667c5c7efcf4c76070e05: passed
  • Changed-head relay-summary mutation check: passed by failing on the raw-hex reversion and passing after restoration
  • Fresh GitHub check at 30cac57f71a374d1176667c5c7efcf4c76070e05: all completed checks passed or were intentionally skipped; all four Desktop Smoke E2E shards, Windows/macOS builds, relay-backed integration shards, DCO, Semgrep, and zizmor were green

Confidence gaps

  • Desktop Domain / Desktop Core remains in progress. Author action: none unless it reports a PR-caused failure. Verification owner: merge owner / CI.
  • A focused browser run reached 70 passing tests before reviewer-worktree cleanup terminated workers; 182 tests did not run, so it is not claimed as a complete pass. No fresh native Tauri visual artifact was produced. These are reviewer-verification gaps, not evidence of an author defect. Author action: none. Verification owner: reviewer or release owner if additional native/browser evidence is required.

No author rework requested. Approval is appropriate once the remaining required exact-head CI gate is terminal green. Any new head invalidates this review.

loganj added a commit that referenced this pull request Sep 9, 2026
…gate, strict parser) (#7488)

🤖
## Summary

Identity keys in the desktop app are displayed as raw 64-character hex.
A person's key shows up as something like `953d3363…` — unreadable,
impossible to recognize as the same identity on another screen, and a
hazard when copied by hand. Nostr (the protocol Buzz runs on) has a
human-readable spelling for identity keys — the `npub1…` form — but the
desktop app did not use it consistently.

This is the foundation of the desktop npub changes: it adds the shared
pieces every identity surface builds on, and two follow-up slices stack
directly on this branch — #7489 converts the identity controls (profile,
settings, allowlist, workflow key fields) and #7495 converts the
everyday display surfaces (mentions, member lists, sidebar, and other
name fallbacks).

After this change:

- The shared identity widget shows the compact npub form —
`npub1j57...fjmv` — instead of a hex prefix, everywhere it renders (for
example the owned-agent public-key row on a profile). Copying it puts
the full npub on the clipboard.
- Copy is a real interaction, verified end-to-end: both popover variants
put the exact canonical npub on the actual clipboard — never the raw hex
the popover also lists, never a truncation — and a portaled popover's
clicks no longer steal focus from the new-DM To-field mid-copy. Pointer
copy, a natural Space-then-Enter path, and inner/outer Escape are
covered.
- Anything that isn't a valid identity key fails neutrally: short or
corrupt values — including degenerate values that technically encode to
a checksum-valid npub but aren't real identity keys — show "Unavailable"
with no copy button, instead of a misleading value.
- Both valid npub spellings display: all-lowercase `npub1…` and
all-uppercase `NPUB1…` (Bech32, npub's encoding, permits either casing)
both render the same canonical lowercase npub. Mixed case is rejected by
the display path as written — `canonicalNpub` and the widget don't
case-normalize input — while input parsing (`parsePubkeyInput`) keeps
its trim-and-lowercase normalization and accepts mixed-case npubs; both
paths require the decoded payload to be exactly a 64-character identity
key.
- Identity-key input is strict on payload: an npub whose decoded payload
isn't exactly a 64-character identity key is rejected, matching the
validation the app's Rust side already applies to agent allowlists.

Intentional scope boundary: only surfaces that render through the shared
widget change here. Outer profile copy, settings identity cards, the
respond-to allowlist, and workflow key fields still show hex — they move
to npub in the controls follow-up (#7489). Nothing else changes identity
representation: display names, private keys, event IDs, and the hex the
app stores, sends, and matches internally are untouched; only the
user-facing spelling of an identity key changes.

## Details

- `desktop/src/shared/lib/pubkey.ts` — `canonicalNpub()`: strict
canonical full-npub helper (64-char hex in any case, or a
checksum-validated npub, returns the canonical npub; anything else
returns `null`); `truncateNpub()`: the compact display form; existing
exports unchanged.
- `desktop/src/shared/ui/PubKey.tsx` — the shared widget's identity gate
validates through `canonicalNpub`; the popover copies the npub only.
- `desktop/src/shared/lib/nostrUtils.ts` — `parsePubkeyInput` rejects
npubs whose payload is not exactly a 64-character identity key.
- `desktop/src/features/messages/ui/NewMessageScreen.tsx` — the To-field
focuses its search input only for clicks that land inside the field
itself, so portaled recipient popovers keep their focus while open (a
popover click previously dismissed it mid-copy).
- Unit suites cover the helper, widget, and parser (including the
degenerate-encode and uppercase regressions); the e2e specs that render
these rows assert the npub display.

### Related issue

- Fixes: N/A. Searched existing issues/PRs for npub identity display —
no existing match.
- Stack: #7489 is based on this branch and builds on these primitives;
it does not stand alone on main.

### Testing

At head `b3310c248` (base: main `44316ff72`; 12 files, +440/−39):

- Focused unit suites (pubkey, PubKey, parsePubkeyInput): 20/20 green;
mutation-checked — removing the decoded-length predicate fails the
short/empty checksum-valid-npub assertions in `canonicalNpub` and the
widget, and a wrong-identity clipboard value fails the new copy
assertions.
- `pnpm typecheck` and `pnpm check`: pass; full desktop unit suite
6459/6459 at this exact head.
- Targeted e2e at this exact head: 8/8 across the two specs that own the
clipboard flows — `agent-access-warning.spec.ts` (compact variant,
agent-access owner hint) and `pubkey-display-screenshots.spec.ts` (full
variant, new-DM recipient verification: pointer copy, popover surviving
the copy, inner/outer Escape, Space-then-Enter).
- No Rust-side or build files change in this PR, so those results are
unaffected.

### Task provenance

Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9`

Task:
buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Base automatically changed from fix/desktop-npub-identity-d1a to main September 9, 2026 14:32
@loganj
loganj dismissed jedwards27’s stale review September 9, 2026 14:32

The base branch was changed.

loganj and others added 5 commits September 9, 2026 10:41
PR-D2 of the npub identity display standardization (stacks on PR-D1's
truncateNpub/PubKey foundation).

## Summary

- Mention identity: mentionDisplay/markdown render compact npubs
  (truncateNpub); copied-mention and clipboard qualification keeps
  byte-exact identity roundtrips for both npub and legacy hex-truncated
  chips, rejecting wrong/missing/tampered key qualification.
- Member/participant surfaces (channels, community members, huddle,
  projects, pulse, search, sidebar, tray, notifications): identity
  fallbacks render compact npubs; titles/aria stay machine-readable
  full labels.
- Profile labels: resolveUserLabel and formatOwnerLabel fall back to
  compact npub (never raw hex); profile panel/popover identity rows and
  copy actions follow.
- Workflows: author picker secondary labels, step destination keys, and
  trigger author references render compact npubs while event/blob IDs
  keep truncatePubkey; roles/templates pass through.
- Avatar initials: key-form labels derive initials from the key tail
  (validated full npub or exact compact npub shape over the bech32
  alphabet) so key-fallback avatars stay distinct; authored names that
  merely resemble npubs keep name-path initials.
- Unit tests across all touched surfaces pin npub compacts, invalid-key
  behavior, and legacy-hex chip roundtrips; e2e specs assert npub
  compacts for author options, mention chips, hover identity, and
  profile owner rows.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
… labels

Astra P2 follow-up on PR-D2. Generated role-prefixed fallback labels
("Participant npub1…", "Agent npub1…") are ordinary words to getInitials,
so every unnamed participant/agent avatar collapsed onto the same "PN"/"AN"
initials — losing the distinct key-tail initials raw hex keys used to
produce.

- ProfileAvatar/UserAvatar: optional initialsLabel (defaults to label /
  displayName) drives fallback initials only; alt text, visible label, and
  the displayName color hash are unchanged.
- ParticipantList and ChannelActivityPopover working-agent rows keep the
  visible "Agent/Participant npub1…" display but pass the unprefixed
  compact key as initialsLabel, so key-fallback avatars render distinct
  key-tail initials; authored names keep name initials.
- WorkingAgentRows exported for consumer tests.
- Consumer regressions: ParticipantList.test.mjs mounts the real
  HuddleParticipantsControl (CommunitiesProvider + relay-agents query
  wiring); ChannelActivityPopover.test.mjs mounts WorkingAgentRows with a
  real root and the production fallback-delay path. Both pin distinct
  key-tail initials (RC/04, not PN/AN) and authored name initials (AE/HR).

Validation at final state (rebased onto D1a 2c68ddd): pnpm check,
pnpm typecheck, full desktop unit suite (6476 pass, incl. these 4),
focused mock-bridge e2e — workflow author picker (2), pubkey-display
key-row/widget-scope (4), mentions profile panel/popover (2),
mention-recipient qualified chips (8) all pass; workflow-local-controls:149
snapshot remains the documented preexisting 438px darwin drift
(pinned-baseline-equivalent, not regenerated).

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Tests-only cleanup for PR-D2 per the proportionality audit. No production
change: every edit lands in unit suites and the two existing e2e specs,
so the slice's build, typecheck, and unit evidence still binds.

## Summary

- Delete the dedicated consumer drivers added with the initials fix
  (ParticipantList.test.mjs and ChannelActivityPopover.test.mjs, 381
  lines): their minimal real-avatar assertions move into the harnesses
  that already mount the real UI — the huddle roster and the
  channel-activity mock-UI e2e specs now pin named authored controls
  (initials AL/C) and unnamed key-fallback rows (key-tail initials
  X2/SQ/RC/04, never the collapsed PN/AN).
- Slim the npub unit matrices in mentionClipboard, timelineMentionCopy,
  identity, workflowStepDescription, workflowTriggerDescription,
  initials, and mentionDisplay suites, preserving the load-bearing
  cases: clipboard byte-exact identity roundtrips for new and legacy
  chips (wrong/missing/tampered qualification rejected), initials
  lookalike gating and checksum-valid key tails, authored-name paths,
  and workflow author/eventID boundaries.

Validation at final state (tests-only edits; predecessor evidence
reused, no suite escalation): 9 changed/related unit files 100/100;
typecheck, e2e build, biome, and px text/truncation checks clean;
huddle roster focused run green; channel-activity full e2e 11/11
green. Wiring mutation checks that failed huddle (X2 collapsing to PN)
and activity (RC missing/AN) were restored and the rebuild was green.
Known limitation, unchanged by this cleanup: full huddle-transcription
e2e remains 25 pass / 1 fail on distinct agent voices at the voice
menu, reproduced on pristine HEAD via stash run (preexisting, outside
this cleanup); workflow-local-controls:149 keeps the documented
preexisting 438px darwin caret drift (separate baseline, not
regenerated). The last edit was a biome line-join in the huddle spec
only (formatting-only, no rerun for it).

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Tests-only fix for PR-D2 per Astra review of the revised candidate
(approved 4b2a046b; the original 4d9add22 candidate was rejected and
not applied). No production change: both edits land in the existing
members-sidebar virtualization spec in channels.spec.ts.

## Summary

- First-window endpoint: generated members have no display name, so
  the roster sorts them by their npub fallback label; sequential
  pubkeys with a shared `npub1qqq...` prefix order by checksum tail,
  not numeric position. Resolving the first generated row from the
  pubkeys the initial window actually rendered replaces the unstable
  `pubkeys[0]` assumption.
- Tail endpoint: the fully-scrolled tail check no longer samples
  `pubkeys.at(-1)` (subject to window luck). The sidebar's own
  "Members - N" header must equal the fixture-known roster total
  (3 seeded + 500 generated), and the final row is located by
  VirtualizedList's stamped `data-index` on the last item, a fixed
  target a virtualizer clamped mid-roster can never render.

Validation at this exact delta (tests-only, evidence reused from the
approved candidate; no full suite run, no re-review): focused
virtualization spec 1/1 green, members-sidebar shard 12/12 green,
biome and e2e build/typecheck clean. A midpoint negative control
(skipping to the middle of the roster) fails the new tail endpoint.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
jedwards27 review of PR #7495 (exact head a1ffa77, 22:10:51Z) flagged
[P2] that buildRelayThreadSummary fell back from a missing profile name
to the raw pubkey, so a cold/relay-only thread facepile exposed the raw
64-char hex identity via UserAvatar's displayName label while the
client-assembled path (resolveUserLabel) already renders the compact
npub.

Fall back to truncateNpub(pubkey) instead — the same canonical compact
identity form used everywhere else in this PR. Authored profile names
are untouched; distinct key-tail avatar initials come for free because
getInitials derives them from the compact npub shape.

Replace the review-flagged invalid relay-summary fixture ("bob")
with a valid 64-hex key and assert the compact npub label — the value
MessageThreadSummaryRow binds to UserAvatar's visible/accessible
displayName. Verified falsifiable: reverting the fallback to raw
pubkey fails the test.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj
loganj force-pushed the fix/desktop-npub-identity-d2 branch from 30cac57 to bd05f63 Compare September 9, 2026 14:47
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is bfc384855889432df4a333a0edf3080f332ee169...236af9e6137386737e84d3a474d6bc808a704c50.
A new review must complete for this exact range. When manual authorization
is required, a Block organization member must comment exactly
@buzz-security-review 236af9e6137386737e84d3a474d6bc808a704c50 to authorize a new review.
Any previous review applies only to its recorded range.

@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 — APPROVE

Reviewed: bfc384855889432df4a333a0edf3080f332ee169..bd05f631fc1ecd10307ee85234db56a474f7206f (exact head bd05f631fc1ecd10307ee85234db56a474f7206f)

Risk: medium — user-visible participant identity fallback in the thread summary.

Behavior/contracts traced: relay participant full-key ownership/deduplication, named and unnamed labels, validated canonical npub fallback, avatar consumption, ordering/counts, local-summary override, invalid-input recovery, and exact production regression seam.

Findings: no blocking code/product defect. The changed-head delta is limited to threadPanel.ts and threadPanel.test.mjs: relay participants retain full pubkeys as identity keys while unnamed presentation uses validated truncateNpub; invalid input fails closed to Unavailable, not raw key text. The regression drives buildMainTimelineEntries and mutation back to raw pubkey fails the intended assertion.

Author action: none.

Verification owner: CI for terminal Desktop Core and failed Smoke E2E shard 4 triage/retry; reviewer/release owner for optional native visual observation. The current CI state is an external confidence/gate item, not an established author-actionable defect, so it does not convert sound code into REQUEST CHANGES.

Validation

At clean exact head:

  • just desktop-test — PASS, 6,471/6,471.
  • just desktop-typecheck, just desktop-check, just desktop-build — PASS.
  • Focused production-seam test — PASS, 23/23; mutation to raw pubkey failed exactly the unnamed relay participant assertion, restoration passed and tree returned clean.
  • git diff --check — PASS.
  • Relevant CI builds, integration, relay E2E, DCO, Semgrep, and zizmor were green; Desktop Core remained pending and Smoke E2E shard 4 reported failure while the overall run was still active and logs unavailable.
  • Live head remained exact and mergeable before submission.

Manual/native evidence: no fresh native visual journey. This delta changes deterministic display conversion only; no IPC, persistence, network, async lifecycle, or native code changed.

Residual risk: the terminal Smoke E2E failure cause is not yet available. CI owns merge readiness and must distinguish flaky/infrastructure behavior from a PR-caused required-gate failure.

@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 — changed-head re-review

Verdict: REQUEST CHANGES at exact head bd05f631fc1ecd10307ee85234db56a474f7206f against base bfc384855889432df4a333a0edf3080f332ee169.

Blocking finding

  • Required Desktop Smoke E2E shard 4 is red on a PR-touched workflow regression. The exact-head run failed desktop/tests/e2e/workflow-local-controls.spec.ts:268 (round-trips and reopens structured message-text conditions) on all three attempts. Attempts 1 and 3 timed out waiting for the edit-workflow trigger control at helper line 56; retry 1 reached the layout assertion at line 298 but found the second operator left of the first (1091.219 versus 1188.740) instead of to its right. This PR changes that spec and the workflow trigger/step description surfaces, so this is a PR-caused required-gate failure until shown otherwise—not merely missing reviewer evidence. The same shard labeled six other tests flaky, but only this test exhausted all retries and made the required check fail.
    • Author action: make the changed workflow behavior/test deterministic and restore the required exact-head shard to green; if the result is infrastructure-only, provide a green exact-head rerun demonstrating that.
    • Verification owner: author for the fix/rerun; reviewer will re-check the resulting exact head and terminal CI.
    • Evidence: https://github.com/block/buzz/actions/runs/34365934552/job/102514840928

Code review and local evidence

No additional concrete defect was found across the systems/integration and product/UI/adversarial review. Relay participants retain full pubkeys as identity keys for merge/deduplication while only unnamed presentation falls back through validated truncateNpub; malformed input fails closed. Mention binding/legacy compatibility, roster/profile/search/new-DM, avatar, huddle/activity, workflow, keyboard, and accessibility paths were reviewed without another blocker.

At the pinned clean head:

  • just desktop-test: 6,471/6,471 passed
  • just desktop-typecheck: passed
  • just desktop-check: passed (existing non-fatal Biome advisories only)
  • just desktop-build: passed, including protected-artifact checks
  • git diff --check: passed
  • Focused relay-summary regression: 23/23 passed; mutation back to raw pubkey failed the intended assertion (22 pass / 1 fail) and restoration returned the tree clean

Fresh GitHub state remained mergeable at the same base/head, with 23 successful checks and no other terminal failure; Desktop Core was still running at final refresh. No fresh native visual/keyboard/AX journey was captured locally because the UI reviewer worktree lacked dependencies; that remains a confidence gap, not separate author rework.

Any new head invalidates this verdict.

loganj and others added 2 commits September 9, 2026 12:15
- addMessageStep fills the step-owned textarea (id wf-step-*-text) instead
  of the ambiguous getByLabel("Message text"), so the fill auto-waits for
  the entering step pane and can never land in the exiting trigger
  condition input during AnimatePresence.
- template-variables spec uses the same owned-textarea locator for the
  identical step-pane fill race (the fill was landing in the exiting
  trigger "Message text" input, so the autocomplete listbox never
  opened).
- message-condition round-trip waits for animations after opening the
  trigger inspector before reading operator bounding boxes, so the ~240ms
  inspector width transition cannot distort sequential box positions.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
The committed darwin image for workflow-template-variable-autocomplete was
recorded at 9390e11 and no longer matches the UI it captures: the name-row
enable switch is gone (activation goes through submit/confirmation) and the
message_posted trigger renders MessageSquare instead of a lightning glyph.
At candidate 1143af3, an isolated build:e2e run (dedicated port 4891)
reproduced the 438-pixel failure whose mask bounds exactly those two intended
changes; the diff rendering is byte-identical to the independently reviewed
historical failure, and today's actual differs from that historical actual by
only 80 pixels of +-1-3 RGB-unit antialiasing noise (0.009%). Baseline
refreshed from that proven candidate actual; the focused test now passes,
including all subsequent keyboard/caret assertions.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>

@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 — APPROVE

Reviewed: bfc384855889432df4a333a0edf3080f332ee169..236af9e6137386737e84d3a474d6bc808a704c50 (exact live head 236af9e6137386737e84d3a474d6bc808a704c50)

Risk: medium — user-visible workflow test synchronization and a Darwin visual baseline on top of broad Desktop identity presentation work.

Findings: no unresolved author-actionable defect. Two independent changed-head review lanes agree that the new synchronization follows production ownership and animation lifecycle rather than hiding failures:

  • workflow-local-controls.spec.ts:67-69,158 targets the entering step-owned textarea by its structural ID. WorkflowStepCard.tsx:147-157,411 generates wf-step-${index}, binds the label to ${prefix}-text, and assigns that ID to the textarea, excluding the departing trigger field that shares the accessible name “Message text.”
  • workflow-local-controls.spec.ts:290-305 uses the repository’s bounded Web Animations helper (tests/helpers/animations.ts:18-30, 1-second ceiling) before geometry assertions against an inspector whose width/content animate. Operator ordering, selected state, exact text, Advanced/Basic round-trip, save, reopen, keyboard behavior, and accessibility assertions remain intact.
  • The refreshed 1280×720 Darwin baseline was inspected before/after. Its visible changes match current production UI: removal of the obsolete enable switch and lightning-to-message icon replacement. The autocomplete list, typed value, labels, caret state, and surrounding layout remain represented. Exact comparison found 5,573 changed pixels (0.6047%); the old baseline is byte-identical to its historical introduction.

Author action: none. Any new head invalidates this approval.

Verification owner: CI/merge owner for terminal exact-head Desktop Core and Smoke E2E shards, especially shard 4. A PR-caused required-gate failure would require renewed author action and review; pending reviewer tooling or gate completion alone is not a code defect.

Exact-head validation

All local runs were bracketed by exact HEAD 236af9e6137386737e84d3a474d6bc808a704c50 and a clean tracked tree:

  • Full workflow-local-controls.spec.ts: 12/12 passed under CI=1 after pnpm build:e2e.
  • Two race-sensitive workflow cases repeated 10 times each: 20/20 passed.
  • Full Desktop unit package: 6,471/6,471 passed.
  • Desktop check and typecheck: passed; only pre-existing non-fatal Biome advisories were reported.
  • git diff --check: passed.
  • Windows/macOS builds, relay-backed integration, DCO, Semgrep, and zizmor were green at final pre-submit refresh.

Residual risk

A compiling mutation reverting only the three synchronization edits also passed the 20-repeat local probe, so local natural-speed execution did not reproduce the earlier CI race. The fix remains source-backed and exact-head candidate coverage is green locally, but stressed exact-head Smoke shard 4 is the load-bearing causal confirmation. No fresh installed-native Tauri journey was required for this test/snapshot-only changed-head delta.

@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 — APPROVE

Reviewed: bfc384855889432df4a333a0edf3080f332ee169..236af9e6137386737e84d3a474d6bc808a704c50 (exact head 236af9e6137386737e84d3a474d6bc808a704c50)

Risk: medium — user-visible workflow E2E synchronization and a Darwin screenshot baseline layered onto a broad Desktop identity-presentation PR.

Behavior/contracts traced: production ownership of workflow step textareas; exiting/entering AnimatePresence panes; trigger-inspector width/content animations and geometry assertions; message-condition save/reopen; template-variable keyboard/listbox/focus/caret semantics; Darwin baseline provenance and visible delta; identity presentation regressions retained from the broader PR.

Findings: no unresolved author-actionable defect. Both assigned review lanes independently found the synchronizers semantically tied to production behavior rather than arbitrary sleeps:

  • The new wf-step-${index}-text locator selects the entering step-owned textarea generated and labeled by WorkflowStepCard, avoiding the exiting trigger condition’s identically named field.
  • waitForAnimations is the repository’s bounded Web Animations helper and runs before operator geometry is read, matching the 240 ms inspector-width / 150 ms content transitions while preserving the positional, selection, round-trip, and reopen assertions.
  • The refreshed 1280×720 Darwin baseline shows the intended current UI changes—removal of the obsolete enable switch and lightning-to-message icon replacement—without hiding the autocomplete, typed value, caret, labels, or layout. The historical pixel-count narrative was not needed for clearance.

Author action: none.

Verification owner: exact-head GitHub Desktop Smoke E2E (4) and Desktop Core gates own the remaining stressed-environment proof. If either reports a PR-caused failure, or if the head changes, this approval expires and requires fresh review.

Exact-head validation

At clean 236af9e6137386737e84d3a474d6bc808a704c50:

  • Full workflow-local-controls.spec.ts: 12/12 passed on isolated port 4891, including screenshot, layout, keyboard, and accessibility assertions.
  • Race-sensitive template-variable and structured-message-condition cases: 20/20 passed across ten repeats.
  • Full Desktop unit package: 6,471/6,471 passed.
  • pnpm -C desktop build:e2e, Desktop check, typecheck, and git diff --check: passed.
  • Windows/macOS builds, relay-backed Desktop integration, DCO, Semgrep, and zizmor: green at final refresh.
  • Authenticated reviewer jedwards27 is distinct from author loganj.

Manual/native evidence: reviewers inspected the old/new Darwin baseline and pixel-difference rendering; no fresh installed-native Tauri journey was produced.

Residual risk: reverting only the three test synchronizers did not reproduce the timing failure locally across twenty repeated cases. The source/lifecycle mapping and candidate runs support the repair, but exact-head Smoke shard 4 remains the load-bearing CI proof under contention. At submission, Desktop Core and Smoke shards 1–4 were still in progress without failure. These are CI/reviewer confidence gaps, not author rework.

@loganj

loganj commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

🤖

Addressing review 5156398401 at published head 236af9e6 (236af9e6137386737e84d3a474d6bc808a704c50, fast-forward from the reviewed bd05f631fc1ecd10307ee85234db56a474f7206f; two test-only commits, no production changes):

  • [Required] shard-4 workflow-local-controls.spec.ts:268 fixed as two test-driver races (commit 1143af345):
    • Timeouts waiting for the edit-workflow trigger control (attempts 1/3): addMessageStep filled via the ambiguous getByLabel("Message text"), which during the ~150ms AnimatePresence exit window resolves to the exiting trigger-condition input; the corrupted saved definition then hung the later wait. The fill now targets the step-owned textarea (textarea[id^="wf-step-"][id$="-text"]), so it auto-waits for the entering step pane and can never land in the trigger field. The identical fill race in the template-variables spec got the same owned-textarea locator.
    • Retry-1 geometry (second operator at 1091.219 left of the first at 1188.740): operator boundingBoxes were sampled partway through the 240ms inspector width transition because animation settling ran only after the comparisons. The round-trip now waits for animations immediately after opening the trigger inspector, before reading boxes, so the transition cannot invert their order.
    • No assertion, threshold, or timeout was loosened; trigger-intended getByLabel("Message text") bindings are byte-identical — test-driver fix only, no product change.
  • One stale Darwin snapshot baseline, independently justified (commit 236af9e6): the workflow-template-variable-autocomplete 438px mismatch is not a product regression. The Darwin expected image was stale: source at 9390e11c9 already renders the no-switch/message-icon state (the name-row enable switch is gone; message_posted renders MessageSquare instead of a lightning glyph). An isolated reproduction at 1143af345 failed with the identical 438px mask bounding exactly those two regions, with a diff byte-identical to the independently reviewed historical failure and an actual differing from that historical actual only by sub-threshold antialiasing (~80 px, ±1–3 RGB units). Only the Darwin baseline was refreshed from that run's actual; the Linux snapshot, thresholds, and assertions are untouched.

Local evidence at these commits: the round-trip test (workflow-local-controls.spec.ts:268) 3/3 green and workflows.spec.ts 29/29; the full workflow-local-controls.spec.ts at 1143af345 was 11 passed / 1 failed, the sole failure being the stale snapshot above; after the refresh, the formerly failing focused screenshot test passed twice consecutively, including its keyboard/caret assertions (the full spec was not rerun after the PNG refresh). Exact-head CI for 236af9e6 is still running and remains the authoritative gate — no re-review is requested until it's green.

@loganj
loganj merged commit ad2a841 into main Sep 9, 2026
57 checks passed
@loganj
loganj deleted the fix/desktop-npub-identity-d2 branch September 9, 2026 18:07
loganj added a commit that referenced this pull request Sep 9, 2026
🤖

## Summary

In the mobile app, anyone who hasn't set a display name shows up as a
raw 64-character hex key (e.g. `3a5d4f9c…`) — unreadable, and
unrecognizable as the same identity across screens. Profile and Settings
also let you copy that raw hex. Nostr public keys have a standard
readable form — `npub1…`, the same encoding other Nostr apps and our
desktop app already display. This PR makes every mobile identity surface
render npub instead:

- **Unnamed people everywhere** — message and thread authors, reactions,
typing indicators, member lists, channel details, DM headers and tiles,
inbox, search, forum cards, Pulse notes and reply context, mention
suggestions, and invite rows — now show a compact npub label: first 8 +
last 4 characters of the full npub joined by an ellipsis
(`npub1abcd…wxyz`), the same truncation desktop uses. Previously these
showed truncated raw hex.
- **DM fallback avatars and blank names** — 1:1 DM tiles and headers key
their fallback avatar to the same non-self counterpart the label names,
including self-first participant order; a self-DM keeps its
hex-key-derived initial. Blank or whitespace-only display names fall
back to the compact npub instead of rendering empty, while nonblank
authored names render verbatim (padding included).
- **Profile sheet → "Copy public key"** now copies the full canonical
npub — never raw hex. When the identity string isn't a valid public key,
the copy tile is disabled, so a malformed key never reaches the
clipboard.
- **Settings → Identity (pubkey)** displays and copies the full npub; an
invalid identity reads "Identity unavailable" with copy disabled.
- **Invalid identities never leak truncated raw hex** into the UI
anywhere — they render a neutral "Unknown identity" label.
- **Unchanged on purpose:** display names and verified handles (NIP-05 —
the `name@domain` badge) still render as before. Unnamed avatars keep
distinct per-key initials, derived from the underlying hex key rather
than the npub — otherwise every unnamed key would render the same "N"
initial. Event IDs are not public keys, so they keep their hex
truncation (in Pulse's "Replying to", the parent author shows npub while
an event-id fallback still shows hex). The nevent share link, private
keys, and internal hex storage are untouched. Inputs that accept a key
(invite/member entry) accept both hex and npub and keep working in hex
internally.

### Related issue

N/A. Searched open issues/PRs for npub identity display on mobile —
closest related: none found. Desktop's parallel npub standardization
lives in the stacked desktop PRs (#7488 foundation, #7489 controls,
#7495 display surfaces); this is the independent mobile slice (based
directly on `main`, not on those branches).

### Testing

At exact head `5a620e420a1fd57d9d8011ac26434eed32fcf765` (base: `main`
`44316ff72`; 40 files, +1,345/−154):

- Full mobile suite: 2,098 tests passing (`cd mobile && flutter test`);
`flutter analyze` clean; `dart format --set-exit-if-changed .` clean —
the same checks CI runs.
- Widget/unit coverage at production seams: compact labels and hex-keyed
avatar initials for DM headers/tiles, member rows, mention suggestions,
and Pulse reply context; DM fallback avatars keyed to the labeled
counterpart (self-first order and self-DMs); blank/whitespace
display-name npub fallback with nonblank authored labels verbatim,
including the Activity inbox sender and profile-sheet heading (each with
its own empty/whitespace production-seam regression); full-npub copy and
disabled-copy semantics in profile and settings; invalid-key
suppression; and hex↔npub input round-trips.

Verified via unit and widget tests — no device/simulator validation is
claimed.

### Task provenance

Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9`

Task:
buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
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