Skip to content

feat(catalog): resolve publisher display name in catalog detail pane - #3640

Merged
wpfleger96 merged 2 commits into
mainfrom
hayt/catalog-owner-display-name
Jul 30, 2026
Merged

feat(catalog): resolve publisher display name in catalog detail pane#3640
wpfleger96 merged 2 commits into
mainfrom
hayt/catalog-owner-display-name

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 29, 2026

Copy link
Copy Markdown
Member

The catalog detail pane hardcoded "Community member" for every non-own catalog entry. The publisher pubkey (catalogSource.ownerPubkey) was already on every entry — it just was not being resolved to a name.

What changed

desktop/src/features/agents/ui/PersonaCatalogDialog.tsx

PersonaCatalogDetail now calls useUsersBatchQuery([ownerPubkey]) when the selected entry is a community (non-own) catalog agent. The label derivation is extracted into the exported pure function resolveCatalogOwnerLabel and uses truthy fallbacks to handle empty or whitespace-only kind:0 fields:

  • Own entry → "You" (unchanged)
  • displayName present and non-blank → the display name
  • displayName absent/blank but name present and non-blank → the name
  • Loading, unresolvable, or both candidates blank → "Community member" (fallback preserved)

The batch query is disabled (enabled: false) when the entry is not a community entry, so there is no extra network call for own entries or built-in agents.

desktop/src/features/agents/ui/personaCatalogOwnerLabel.test.mjs

Unit tests for resolveCatalogOwnerLabel covering: populated displayName wins; whitespace-only displayName falls through to name; both candidates empty/whitespace/null/undefined all fall through to "Community member".

desktop/tests/e2e/agents.spec.ts

  • Updated the existing assertion — it previously checked for the hardcoded fallback; now asserts the resolved mock display name "alice".
  • Added "catalog detail shows Community member when the publisher profile cannot be resolved" — installs a catalog event from an unknown pubkey and asserts the fallback still renders.

@wpfleger96
wpfleger96 requested a review from a team as a code owner July 29, 2026 23:11
npub1g8493u0xfsjrvflg4n08ezd7vec99mnwzlv0qgwpr9d7gvjwhuzqx59rhw and others added 2 commits July 30, 2026 11:10
The `PersonaCatalogDetail` component hardcoded the label passed to
`PersonaAddedBy` as "Community member" for any non-own catalog entry.
The publisher's pubkey is already available as
`catalogSource.ownerPubkey` (set from the kind:30175 event's pubkey).

Use `useUsersBatchQuery` to resolve the pubkey to a kind:0 display
name. Label precedence: own entry → "You"; resolved profile →
`displayName` then `name`; loading or unresolvable → "Community
member" (unchanged fallback).

The batch query is disabled when the entry is not a community entry, so
there is no extra network call for own entries or built-in agents.

E2e: update the existing assertion that was checking for the hardcoded
fallback, add a new test that verifies "Community member" still renders
when the publisher pubkey is not in the mock profile registry.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…ests

Thufir (review at b570318) found that a present-but-empty or
whitespace-only kind:0 `displayName` is not nullish, so the
`??` chain stopped early and the detail pane rendered 'Added by '
with a blank label.

Replace `??` with truthy short-circuit on trimmed values, and extract
the derivation into `resolveCatalogOwnerLabel` so it can be exercised
directly.

Unit tests cover: populated displayName wins; whitespace-only displayName
falls through to name; both candidates empty/whitespace/null/undefined
all fall through to 'Community member'.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the hayt/catalog-owner-display-name branch from b570318 to f1a03bd Compare July 30, 2026 15:12
@wpfleger96
wpfleger96 merged commit 02be413 into main Jul 30, 2026
25 checks passed
@wpfleger96
wpfleger96 deleted the hayt/catalog-owner-display-name branch July 30, 2026 15:38
wpfleger96 added a commit that referenced this pull request Jul 30, 2026
…chive

* origin/main: (22 commits)
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)
  Refine agent sharing dialog (#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (#3607)
  fix: align responsive agent views (#3688)
  Add macOS agent menu-bar menu (#3565)
  Fix pending message feedback (#3543)
  fix(desktop): remove remaining Projects panel fills (#3742)
  feat(mobile): desktop-parity emoji and thread experience (#3485)
  desktop: restore direct community member adds (#3634)
  fix(desktop): explain open agent access (#2561)
  fix(cli): resolve agents from owner records (#3178)
  fix(desktop): remove Projects overview card fills (#3416)
  feat(replica): portable heartbeat-token fence with snapshot-local reader routing (#3268)
  fix(git): channel binding tooling + author remediation for unbound repos (#3626)
  feat: configure S3 URL addressing style (#3400)
  feat: add first-class OpenRouter provider support (#1975)
  feat(agent,acp): wire provider total_tokens through NIP-AM publish chain (#3593)
  chore(release): release Buzz Desktop version 0.5.2 (#3624)
  ...

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jul 30, 2026
…g-pipeline

* origin/main:
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)

Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
joahg added a commit to joahg/buzz-dev-mode that referenced this pull request Jul 30, 2026
…-style

* origin/main:
  fix(acp): preserve truncated thread context (block#3340)
  feat(catalog): resolve publisher display name in catalog detail pane (block#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of block#3467) (block#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (block#3163)
  Refine agent sharing dialog (block#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (block#3607)
  fix: align responsive agent views (block#3688)
  Add macOS agent menu-bar menu (block#3565)
  Fix pending message feedback (block#3543)
  fix(desktop): remove remaining Projects panel fills (block#3742)
  feat(mobile): desktop-parity emoji and thread experience (block#3485)
  desktop: restore direct community member adds (block#3634)
  fix(desktop): explain open agent access (block#2561)
  fix(cli): resolve agents from owner records (block#3178)
  fix(desktop): remove Projects overview card fills (block#3416)
  feat(replica): portable heartbeat-token fence with snapshot-local reader routing (block#3268)

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
wpfleger96 added a commit that referenced this pull request Jul 30, 2026
* origin/main: (59 commits)
  Fix video reviews in thread replies (#3719)
  feat(release): make desktop releases immutable (#3568)
  Make relay reconnect backoff authoritative (#3774)
  feat(desktop): add password-protected backups in settings (#3701)
  fix(desktop): reuse profiles when joining communities (#2155)
  Render mobile agent mention chips (#3702)
  fix(catalog): update Amp description (#3758)
  fix(acp): preserve truncated thread context (#3340)
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)
  Refine agent sharing dialog (#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (#3607)
  fix: align responsive agent views (#3688)
  Add macOS agent menu-bar menu (#3565)
  Fix pending message feedback (#3543)
  fix(desktop): remove remaining Projects panel fills (#3742)
  feat(mobile): desktop-parity emoji and thread experience (#3485)
  desktop: restore direct community member adds (#3634)
  fix(desktop): explain open agent access (#2561)
  ...

# Conflicts:
#	desktop/scripts/check-file-sizes.mjs
wpfleger96 pushed a commit that referenced this pull request Jul 30, 2026
* origin/main: (29 commits)
  feat(desktop): raise the install ceiling and make installs observable (#3368)
  fix(db): isolate usage metrics advisory-lock test on scratch DB (#3670)
  Add Devin as a preset ACP harness (#3225)
  feat(desktop): improve agent activity header ui (#3321)
  perf(presence): reduce heartbeat frequency (#3783)
  Tighten continuation message rows (#3724)
  Fix video reviews in thread replies (#3719)
  feat(release): make desktop releases immutable (#3568)
  Make relay reconnect backoff authoritative (#3774)
  feat(desktop): add password-protected backups in settings (#3701)
  fix(desktop): reuse profiles when joining communities (#2155)
  Render mobile agent mention chips (#3702)
  fix(catalog): update Amp description (#3758)
  fix(acp): preserve truncated thread context (#3340)
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)
  Refine agent sharing dialog (#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (#3607)
  fix: align responsive agent views (#3688)
  ...

Signed-off-by: npub1g8493u0xfsjrvflg4n08ezd7vec99mnwzlv0qgwpr9d7gvjwhuzqx59rhw <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
wpfleger96 added a commit that referenced this pull request Jul 31, 2026
Main's agent-sharing UI changed during the branch's review arc:

- PersonaShareDialog (PR #3699, Jul 30): replaced the catalog-share
  dropdown (SnapshotOptionMenu "Not shared"/"Shared") with a plain Switch
  (aria-label="Share to catalog", checked = shared).
- PersonaCatalogDialog (PR #3640): added resolveCatalogOwnerLabel +
  useUsersBatchQuery to the detail pane, showing "Added by <display name>"
  instead of a hardcoded "Community member" fallback.

TeamShareDialog: adopt the Switch pattern, removing the SnapshotOptionMenu
and the now-unused teamCatalogCopy.shareAriaLabel / notSharedOption /
sharedOption keys.

TeamCatalogDialog: wire resolveCatalogOwnerLabel + useUsersBatchQuery into
TeamCatalogDetail, mirroring PersonaCatalogDetail exactly. Community entries
now resolve the owner's display name / username; own entries still show "You".

Full audit performed against PersonaShareDialog and PersonaCatalogDialog at
origin/main — no other structural divergences found beyond these two.

Playwright specs updated: setTeamCatalogAccess now uses Switch semantics
(.isChecked() / .click()), the share-access-menu screenshot removed (no open
dropdown in the Switch pattern), and the "Added by" assertion updated to the
resolved username.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jul 31, 2026
…unity catalog

Adds kind:30178 projection builder and v1 size contract, publish/unshare/
tombstone, workspace-apply freshness with automatic retraction, atomic
add_team_from_catalog with backend head verification, member provenance,
TeamShareDialog/catalog UI, and reuse of the persona-sharing machinery.

Key design decisions and invariants:
- v1 size contract: MAX_TOTAL_BYTES 192 KiB, MAX_SYSTEM_PROMPT_BYTES 16 KiB,
  MAX_AVATAR_URL_BYTES 32 KiB, MAX_MEMBERS 64.
- Member identity: SHA-256 of (owner_pubkey || persona_id) as member_key,
  preventing key collapse across publishers.
- Projection freshness: workspace-apply rebuilds retained shared 30178 heads;
  failed rebuilds (size violation, missing member, resolution failure)
  immediately purge+tombstone the shared head and emit a typed notice.
- Allowlist mode normalized to owner-only at adoption (source pubkeys never
  published; empty allowlist would fail mint).
- Built-in avatar normalization: oversized install-local avatars silently
  stripped for canonical built-ins; non-built-ins get deterministic rejection.
- Transactional purge+tombstone: DELETE + INSERT kind:5 in one BEGIN IMMEDIATE
  transaction so a process kill cannot leave a relay head shared indefinitely.
- Byte-level rollback: both persona and team stores snapshot before any write;
  independent restores on failure with error aggregation.
- Agent-ref preservation: catalog copy deactivation skips any copy whose ID
  is referenced by a remaining team's persona_ids OR a managed agent's
  persona_id — prevents deleting a catalog team from breaking a standalone agent.
- URL safety contract: UTF-8 byte cap (2048) → ECMAScript-\s/paren rejection
  (exact WHATWG equivalence: char::is_whitespace() - U+0085 + U+FEFF) →
  url::Url::parse → normalized http/https scheme check. Identical algorithm
  on both Rust and TypeScript sides proven by 4200-case cross-runtime corpus.
- E2E tie-break: mock bridge returns events in created_at DESC, id ASC order
  matching relay behavior for equal-second arrivals.
- UI aligned with current agent-sharing patterns: TeamShareDialog uses Switch
  (not SnapshotOptionMenu) for the catalog-share control, matching
  PersonaShareDialog post-#3699; TeamCatalogDialog uses resolveCatalogOwnerLabel
  + useUsersBatchQuery for publisher attribution, matching PersonaCatalogDialog
  post-#3640.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jul 31, 2026
…unity catalog

Adds kind:30178 projection builder and v1 size contract, publish/unshare/
tombstone, workspace-apply freshness with automatic retraction, atomic
add_team_from_catalog with backend head verification, member provenance,
TeamShareDialog/catalog UI, and reuse of the persona-sharing machinery.

Key design decisions and invariants:
- v1 size contract: MAX_TOTAL_BYTES 192 KiB, MAX_SYSTEM_PROMPT_BYTES 16 KiB,
  MAX_AVATAR_URL_BYTES 32 KiB, MAX_MEMBERS 64.
- Member identity: SHA-256 of (owner_pubkey || persona_id) as member_key,
  preventing key collapse across publishers.
- Projection freshness: workspace-apply rebuilds retained shared 30178 heads;
  failed rebuilds (size violation, missing member, resolution failure)
  immediately purge+tombstone the shared head and emit a typed notice.
- Allowlist mode normalized to owner-only at adoption (source pubkeys never
  published; empty allowlist would fail mint).
- Built-in avatar normalization: oversized install-local avatars silently
  stripped for canonical built-ins; non-built-ins get deterministic rejection.
- Transactional purge+tombstone: DELETE + INSERT kind:5 in one BEGIN IMMEDIATE
  transaction so a process kill cannot leave a relay head shared indefinitely.
- Byte-level rollback: both persona and team stores snapshot before any write;
  independent restores on failure with error aggregation.
- Agent-ref preservation: catalog copy deactivation skips any copy whose ID
  is referenced by a remaining team's persona_ids OR a managed agent's
  persona_id — prevents deleting a catalog team from breaking a standalone agent.
- URL safety contract: UTF-8 byte cap (2048) → ECMAScript-\s/paren rejection
  (exact WHATWG equivalence: char::is_whitespace() - U+0085 + U+FEFF) →
  url::Url::parse → normalized http/https scheme check. Identical algorithm
  on both Rust and TypeScript sides proven by 4200-case cross-runtime corpus.
- E2E tie-break: mock bridge returns events in created_at DESC, id ASC order
  matching relay behavior for equal-second arrivals.
- UI aligned with current agent-sharing patterns: TeamShareDialog uses Switch
  (not SnapshotOptionMenu) for the catalog-share control, matching
  PersonaShareDialog post-#3699; TeamCatalogDialog uses resolveCatalogOwnerLabel
  + useUsersBatchQuery for publisher attribution, matching PersonaCatalogDialog
  post-#3640.
- Detail pane exposes full configuration: each member row is expandable
  (accordion with aria-expanded, keyboard-operable) showing AgentDefinitionMetadata
  (model/runtime/provider) + member system prompt; team-level instructions render
  below the description when set. AgentDefinitionMetadata gains an optional
  provider prop used by both catalogs (persona and team), rendering
  "Preferred provider" cell when present.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
calvadev pushed a commit to shopstr-eng/buzz that referenced this pull request Aug 3, 2026
…lock#3640)

The catalog detail pane hardcoded "Community member" for every non-own
catalog entry. The publisher pubkey (`catalogSource.ownerPubkey`) was
already on every entry — it just was not being resolved to a name.

## What changed

**`desktop/src/features/agents/ui/PersonaCatalogDialog.tsx`**

`PersonaCatalogDetail` now calls `useUsersBatchQuery([ownerPubkey])`
when the selected entry is a community (non-own) catalog agent. The
label derivation is extracted into the exported pure function
`resolveCatalogOwnerLabel` and uses truthy fallbacks to handle empty or
whitespace-only kind:0 fields:

- Own entry → `"You"` (unchanged)
- `displayName` present and non-blank → the display name
- `displayName` absent/blank but `name` present and non-blank → the name
- Loading, unresolvable, or both candidates blank → `"Community member"`
(fallback preserved)

The batch query is disabled (`enabled: false`) when the entry is not a
community entry, so there is no extra network call for own entries or
built-in agents.

**`desktop/src/features/agents/ui/personaCatalogOwnerLabel.test.mjs`**

Unit tests for `resolveCatalogOwnerLabel` covering: populated
`displayName` wins; whitespace-only `displayName` falls through to
`name`; both candidates empty/whitespace/null/undefined all fall through
to `"Community member"`.

**`desktop/tests/e2e/agents.spec.ts`**

- Updated the existing assertion — it previously checked for the
hardcoded fallback; now asserts the resolved mock display name
`"alice"`.
- Added "catalog detail shows Community member when the publisher
profile cannot be resolved" — installs a catalog event from an unknown
pubkey and asserts the fallback still renders.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1g8493u0xfsjrvflg4n08ezd7vec99mnwzlv0qgwpr9d7gvjwhuzqx59rhw <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
wpfleger96 added a commit that referenced this pull request Aug 3, 2026
…unity catalog

Adds kind:30178 projection builder and v1 size contract, publish/unshare/
tombstone, workspace-apply freshness with automatic retraction, atomic
add_team_from_catalog with backend head verification, member provenance,
TeamShareDialog/catalog UI, and reuse of the persona-sharing machinery.

Key design decisions and invariants:
- v1 size contract: MAX_TOTAL_BYTES 192 KiB, MAX_SYSTEM_PROMPT_BYTES 16 KiB,
  MAX_AVATAR_URL_BYTES 32 KiB, MAX_MEMBERS 64.
- Member identity: SHA-256 of (owner_pubkey || persona_id) as member_key,
  preventing key collapse across publishers.
- Projection freshness: workspace-apply rebuilds retained shared 30178 heads;
  failed rebuilds (size violation, missing member, resolution failure)
  immediately purge+tombstone the shared head and emit a typed notice.
- Allowlist mode normalized to owner-only at adoption (source pubkeys never
  published; empty allowlist would fail mint).
- Built-in avatar normalization: oversized install-local avatars silently
  stripped for canonical built-ins; non-built-ins get deterministic rejection.
- Transactional purge+tombstone: DELETE + INSERT kind:5 in one BEGIN IMMEDIATE
  transaction so a process kill cannot leave a relay head shared indefinitely.
- Byte-level rollback: both persona and team stores snapshot before any write;
  independent restores on failure with error aggregation.
- Agent-ref preservation: catalog copy deactivation skips any copy whose ID
  is referenced by a remaining team's persona_ids OR a managed agent's
  persona_id — prevents deleting a catalog team from breaking a standalone agent.
- URL safety contract: UTF-8 byte cap (2048) → ECMAScript-\s/paren rejection
  (exact WHATWG equivalence: char::is_whitespace() - U+0085 + U+FEFF) →
  url::Url::parse → normalized http/https scheme check. Identical algorithm
  on both Rust and TypeScript sides proven by 4200-case cross-runtime corpus.
- E2E tie-break: mock bridge returns events in created_at DESC, id ASC order
  matching relay behavior for equal-second arrivals.
- UI aligned with current agent-sharing patterns: TeamShareDialog uses Switch
  (not SnapshotOptionMenu) for the catalog-share control, matching
  PersonaShareDialog post-#3699; TeamCatalogDialog uses resolveCatalogOwnerLabel
  + useUsersBatchQuery for publisher attribution, matching PersonaCatalogDialog
  post-#3640.
- Detail pane exposes full configuration: each member row is expandable
  (accordion with aria-expanded, keyboard-operable) showing AgentDefinitionMetadata
  (model/runtime/provider) + member system prompt; team-level instructions render
  below the description when set. AgentDefinitionMetadata gains an optional
  provider prop used by both catalogs (persona and team), rendering
  "Preferred provider" cell when present.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
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.

1 participant