Skip to content

feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues - #4695

Merged
wpfleger96 merged 6 commits into
mainfrom
spec/buzz-entity-links
Aug 4, 2026
Merged

feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues#4695
wpfleger96 merged 6 commits into
mainfrom
spec/buzz-entity-links

Conversation

@thomaspblock

@thomaspblock thomaspblock commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Gives Buzz-hosted git entities the same "GitHub-style" chat experience GitHub links already get: rich preview cards, real titles, and click-through — except clicks navigate in-app to the Projects view instead of a browser.

  • Spec: docs/buzz-entity-links.md — link scheme, slices, and deferred work (buzz://project, OS deep links, web routes).
  • Canonical buzz:// deep links: new desktop/src/shared/lib/entityLink.ts with builders + strict parser for buzz://pr?id=…&owner=…&d=…, buzz://issue?…, and buzz://repo?owner=…&d=…, mirrored by a Rust module (crates/buzz-cli/src/links.rs) with a shared golden-format test so the two implementations can't drift.
  • Preview cards: linkPreview.ts recognizes buzz:// entity links and HTTPS relay clone URLs ({origin}/git/<pubkey>/<repo>, the shape agents paste today). Both normalize onto the canonical buzz:// href, so the two spellings of a repo dedupe to one Buzz-provider card (BuzzMark logo) rendered by link-preview-attachment.tsx.
  • Title enrichment: PR/issue cards fetch the real subject from the relay event (subject tag or first content line) via useResolvedLinkPreviews.ts; the cache is community-scoped and reset in resetCommunityState().
  • In-app navigation: clicking a card or inline anchor (including HTTPS relay clone URLs whose origin matches the active relay) routes to the canonical 30617:<owner>:<d> coordinate via goProject() (markdown/entityLinks.tsx). Merge dependency: feat(projects): support multiple repositories #4671 must merge first — route resolution for 30617: coordinates is implemented on that branch (feat/multi-repository-projects). Entity-link and external-anchor logic were extracted out of markdown.tsx to stay under the file-size ratchet.
  • Agent side: buzz pr open, buzz issues create, and buzz repos create now return a ready-made link field (omitted when the relay returns accepted: false), and base_prompt.md instructs agents to paste it verbatim when announcing work.

Test plan

  • Desktop unit tests: pass, including new entityLink.test.mjs and linkPreview.test.mjs coverage (golden formats, malformed-link rejection, clone-URL/buzz:// dedupe, origin-gated anchor behavior, label-must-win invariant, cache epoch)
  • Rust: cargo test -p buzz-cli golden-format test + accepted/rejected link guard assertions, clippy + fmt clean
  • Biome + tsc --noEmit clean; pre-push hooks (desktop-tauri-checks, rust-tests, desktop-test) pass
  • Manual: paste a relay clone URL and a buzz://pr link in a channel — verify one card each, real PR title, and in-app navigation to the Projects view

Related: #4671

thomaspblock added a commit that referenced this pull request Aug 4, 2026
@thomaspblock

Copy link
Copy Markdown
Contributor Author

Screenshots of the new Buzz entity link cards (captured via the E2E mock bridge).

Repo card from an HTTPS clone URL

An agent pastes the relay clone URL ({origin}/git/<pubkey>/<repo>) — it renders as a Buzz · repo card, and both the card and the inline link navigate in-app to the project.

01-repo-card

PR and issue cards from buzz:// deep links

buzz://pr?… and buzz://issue?… links render as cards with the real subject fetched from the relay event (not the hex fallback).

02-pr-issue-cards

Dedupe: two spellings, one card

The same repository referenced as a clone URL and a buzz://repo deep link in one message produces a single card.

03-dedupe

@thomaspblock
thomaspblock marked this pull request as ready for review August 4, 2026 10:27
@thomaspblock
thomaspblock requested a review from a team as a code owner August 4, 2026 10:27
@thomaspblock
thomaspblock enabled auto-merge (squash) August 4, 2026 10:27
wpfleger96 added a commit that referenced this pull request Aug 4, 2026
Five blocking fixes:

1. urlTransform (buzz:// anchors dead) — replace messageLinkUrlTransform
   with buzzDeepLinkUrlTransform that preserves hrefs passing parseEntityLink.
   Entity link inline anchors now navigate in-app; buzz://connect and other
   non-entity schemes still strip. Adds render-level tests for labeled and
   autolink buzz://pr|issue|repo hrefs including click verification.

2. Route contract (cross-PR break with #4671) — entityLinkProjectRouteId now
   emits canonical 30617:<owner>:<d> coordinates instead of legacy
   <owner>:<dtag>. Duncan's branch resolves 30617 coordinates regardless of
   project grouping, so entity links are stable when a repo changes containers.

3. Arbitrary-host clone-URL rewriting — parseBuzzGitLink now requires
   parsed.origin === activeRelayOrigin before rewriting to buzz://repo.
   evil.example and github.com sharing the /git/<hex>/<repo> path shape stay
   ordinary external links. relayOrigin threaded through parseSupportedLinkPreview,
   extractSupportedLinkPreviews, MarkdownRuntime, and the anchor component.

4. Title-cache reset race — cacheGeneration counter incremented on
   resetLinkPreviewTitleCache(); in-flight promises check their captured
   generation before writing back, preventing stale community titles from
   leaking into the new community's cache after a switch.

5. Title/coordinate trust — fetchBuzzEntityTitle now verifies the fetched
   event's a tag equals 30617:<owner>:<d> before adopting its title.
   A crafted link can no longer pair a real PR title with an unrelated repo.

Three non-blocking fixes:

- Label-must-win on edits: resolvedTitles applied only while
  shouldResolveTitle(preview) still holds, so a bare link edited to
  [My label](same-link) correctly shows the label.

- parseEntityLink hardened: rejects unexpected path segments, fragments,
  duplicate parameters, and unknown query params. Documents the forward-compat
  posture for the reserved relay= field — old clients now decline rather than
  silently misinterpret future extensions.

- CLI link guard: print_create_response now delegates to
  create_response_with_id_if_accepted, omitting the link field when the
  relay returns accepted:false to avoid exposing links to unaccepted events.

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 Aug 4, 2026
## Summary
- adopt the finalized NIP-MP project model so one project can enumerate
and switch between multiple NIP-34 repositories
- add project and repository navigation, activity summaries,
existing-repository attachment, and repository access-channel management
- preserve privacy-safe activation provenance for agent-authored
patches, pull requests, issues, and associated commits

## Test plan
- [x] Run desktop typecheck and unit tests
- [x] Run focused NIP-MP, repository access, and provenance tests
- [x] Run Rust formatting and desktop lint checks
- [x] Run the complete pre-push suite after merging current `main`
- [ ] Manually verify project creation, repository attachment,
switching, and access repair on staging
- [ ] Manually verify public-channel and private-agent origin labels on
newly created Git activity

Related: [#4695](#4695)

---------

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
thomaspblock and others added 6 commits August 4, 2026 17:30
Design doc for buzz:// links to Buzz-hosted repositories, projects, pull
requests, and issues, with GitHub-parity preview cards in chat, in-app and
OS-level deep-link handling, CLI link output, and agent prompt guidance.
Spec only — no implementation.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Relay git URLs ({relay-origin}/git/<pubkey>/<repo>) pasted in chat now get
the same rich preview card treatment as GitHub links. Detection keys on the
/git/<64-hex-pubkey>/<repo> path shape since relay hosts differ per
community; the card links to the browsable web repo page (/repos/<d-tag>)
rather than the raw git transport endpoint. First implemented slice of the
Buzz entity links spec (docs/buzz-entity-links.md).

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Desktop renders buzz://pr|issue|repo links as Buzz preview cards with
titles enriched from the relay event's subject tag, and clicking a card
or inline link navigates in-app to the project detail view. The CLI's
pr open / issues create / repos create now return a matching link field,
and the agent base prompt tells agents to paste it when announcing work.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Clone-URL previews rewrote their href to the relay web page, so clicking
the card opened an external browser instead of the Projects view. Normalize
the href to the canonical buzz://repo deep link, which wires up the same
in-app click handler as explicit entity links, dedupes both spellings of a
repo, and makes inline clone-URL anchors navigate in-app too.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Five blocking fixes:

1. urlTransform (buzz:// anchors dead) — replace messageLinkUrlTransform
   with buzzDeepLinkUrlTransform that preserves hrefs passing parseEntityLink.
   Entity link inline anchors now navigate in-app; buzz://connect and other
   non-entity schemes still strip. Adds render-level tests for labeled and
   autolink buzz://pr|issue|repo hrefs including click verification.

2. Route contract (cross-PR break with #4671) — entityLinkProjectRouteId now
   emits canonical 30617:<owner>:<d> coordinates instead of legacy
   <owner>:<dtag>. Duncan's branch resolves 30617 coordinates regardless of
   project grouping, so entity links are stable when a repo changes containers.

3. Arbitrary-host clone-URL rewriting — parseBuzzGitLink now requires
   parsed.origin === activeRelayOrigin before rewriting to buzz://repo.
   evil.example and github.com sharing the /git/<hex>/<repo> path shape stay
   ordinary external links. relayOrigin threaded through parseSupportedLinkPreview,
   extractSupportedLinkPreviews, MarkdownRuntime, and the anchor component.

4. Title-cache reset race — cacheGeneration counter incremented on
   resetLinkPreviewTitleCache(); in-flight promises check their captured
   generation before writing back, preventing stale community titles from
   leaking into the new community's cache after a switch.

5. Title/coordinate trust — fetchBuzzEntityTitle now verifies the fetched
   event's a tag equals 30617:<owner>:<d> before adopting its title.
   A crafted link can no longer pair a real PR title with an unrelated repo.

Three non-blocking fixes:

- Label-must-win on edits: resolvedTitles applied only while
  shouldResolveTitle(preview) still holds, so a bare link edited to
  [My label](same-link) correctly shows the label.

- parseEntityLink hardened: rejects unexpected path segments, fragments,
  duplicate parameters, and unknown query params. Documents the forward-compat
  posture for the reserved relay= field — old clients now decline rather than
  silently misinterpret future extensions.

- CLI link guard: print_create_response now delegates to
  create_response_with_id_if_accepted, omitting the link field when the
  relay returns accepted:false to avoid exposing links to unaccepted events.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Thread relayOrigin into renderEntityLinkAnchor/resolveEntityHref so that
matching-origin HTTPS clone URL anchors navigate in-app (not just cards).
The first pass left parseSupportedLinkPreview(href) called without an origin
inside resolveEntityHref, causing every HTTPS clone URL to fall through to
ExternalLinkAnchor regardless of origin equality.

Remove dead create_response_with_id() helper — production-dead since
print_create_response moved to create_response_with_id_if_accepted(). The
dead function caused Rust Lint + Windows Rust CI to fail under -D warnings.
Rewrite its unit test with explicit accepted-true and accepted-false
assertions that pin the CLI link-omission behavior.

Export shouldResolveTitle and getLinkPreviewCacheGeneration from
useResolvedLinkPreviews.ts and add the missing regression tests requested
in the original acceptance criteria: cache epoch increments on reset,
fallback-title triggers relay lookup (label-must-win false), and custom
label suppresses relay title lookup (label-must-win true).

Add four renderEntityLinkAnchor behavior tests covering: matching-origin
clone anchor navigates in-app; lookalike origin returns null (external);
no origin returns null (fail closed); direct buzz:// link resolves
regardless of origin.

Update docs/buzz-entity-links.md and PR Summary to document the canonical
30617:<owner>:<d> route contract and the #4671-must-merge-first dependency.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the spec/buzz-entity-links branch from 8e7d784 to 62d6732 Compare August 4, 2026 21:30
@wpfleger96
wpfleger96 disabled auto-merge August 4, 2026 21:31
@wpfleger96
wpfleger96 merged commit a1d78f2 into main Aug 4, 2026
32 checks passed
@wpfleger96
wpfleger96 deleted the spec/buzz-entity-links branch August 4, 2026 21:54
shellz-n-stuff added a commit to shellz-n-stuff/buzz that referenced this pull request Aug 4, 2026
…gent-instructions

* origin/main: (30 commits)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (block#4695)
  fix(desktop): serialize tray channel actions for frontend (block#4762)
  chore(release): release Buzz Desktop version 0.5.5 (block#4788)
  feat(projects): support multiple repositories (block#4671)
  fix(ci): make desktop cache test version agnostic (block#4791)
  fix(desktop): widen post-Enter timeouts in empty-edit-delete spec (block#4792)
  fix(desktop): wait for terminal frame before splash (block#4781)
  fix(desktop): integer-align custom reaction emoji (block#4779)
  Polish Huddle voice controls (block#4694)
  fix(local-archive): default both archive settings to enabled (block#4750)
  fix(mobile): stop oversized read-state retry loop (block#4595)
  fix(desktop): close reconnect gaps that previously required CMD+R (block#4737)
  Dock Buzz Term within channel workspace (block#4724)
  perf(relay): index channel-id lookups and skip trace-only reads (block#4647)
  fix(agents): canonicalize stale persona harness pins (block#4631)
  Refine community invite links (block#4734)
  feat(desktop): persist sidebar observed-unread across webview reload (block#3976)
  feat(desktop): surface config diff in restart-required badge (block#3637)
  Polish sidebar unread hierarchy (block#4573)
  fix(desktop): show cached display names on startup (block#3317)
  ...

Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
tellaho pushed a commit that referenced this pull request Aug 4, 2026
…er-snapshots

* origin/main:
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4797)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (#4695)
  fix(desktop): serialize tray channel actions for frontend (#4762)
  chore(release): release Buzz Desktop version 0.5.5 (#4788)
  feat(projects): support multiple repositories (#4671)
  fix(ci): make desktop cache test version agnostic (#4791)
  fix(desktop): widen post-Enter timeouts in empty-edit-delete spec (#4792)
  fix(desktop): wait for terminal frame before splash (#4781)
  fix(desktop): integer-align custom reaction emoji (#4779)
  Polish Huddle voice controls (#4694)
  fix(local-archive): default both archive settings to enabled (#4750)
  fix(mobile): stop oversized read-state retry loop (#4595)

Co-authored-by: npub14ndfusear8wdpe4kss8h7juc7wjk78atnqzf63zvppcpneknv4sq6x9370 <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: npub14ndfusear8wdpe4kss8h7juc7wjk78atnqzf63zvppcpneknv4sq6x9370 <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>

# Conflicts:
#	desktop/src/features/communities/useCommunityInit.ts
#	desktop/src/shared/lib/linkPreview.test.mjs
#	desktop/src/shared/lib/linkPreview.ts
#	desktop/src/shared/lib/useResolvedLinkPreviews.ts
#	desktop/src/shared/ui/link-preview-attachment.tsx
#	desktop/src/shared/ui/markdown.tsx
tlongwell-block pushed a commit that referenced this pull request Aug 4, 2026
…-overflow-recovery

* origin/main:
  fix: reauthenticate databricks model discovery (#4008)
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4797)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (#4695)

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
tlongwell-block pushed a commit that referenced this pull request Aug 4, 2026
…-overflow-recovery

* origin/main:
  fix: reauthenticate databricks model discovery (#4008)
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4797)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (#4695)

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
sandro-sq added a commit that referenced this pull request Aug 4, 2026
* origin/main: (31 commits)
  feat: paste composer text without formatting (#4801)
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4808)
  chore(release): release Buzz Desktop version 0.5.5 (#4800)
  fix: reauthenticate databricks model discovery (#4008)
  Revert "chore(release): release Buzz Desktop version 0.5.5" (#4797)
  feat: Buzz entity links — rich preview cards + in-app navigation for repos, PRs, and issues (#4695)
  fix(desktop): serialize tray channel actions for frontend (#4762)
  chore(release): release Buzz Desktop version 0.5.5 (#4788)
  feat(projects): support multiple repositories (#4671)
  fix(ci): make desktop cache test version agnostic (#4791)
  fix(desktop): widen post-Enter timeouts in empty-edit-delete spec (#4792)
  fix(desktop): wait for terminal frame before splash (#4781)
  fix(desktop): integer-align custom reaction emoji (#4779)
  Polish Huddle voice controls (#4694)
  fix(local-archive): default both archive settings to enabled (#4750)
  fix(mobile): stop oversized read-state retry loop (#4595)
  fix(desktop): close reconnect gaps that previously required CMD+R (#4737)
  Dock Buzz Term within channel workspace (#4724)
  perf(relay): index channel-id lookups and skip trace-only reads (#4647)
  fix(agents): canonicalize stale persona harness pins (#4631)
  ...

Signed-off-by: Alessandro Joabar <sandro@squareup.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.

2 participants