## 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: #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
- [x] 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)
- [x] Rust: `cargo test -p buzz-cli` golden-format test +
accepted/rejected link guard assertions, clippy + fmt clean
- [x] 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](https://github.com/block/buzz/pull/4671)
---------
Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1g8493u0xfsjrvflg4n08ezd7vec99mnwzlv0qgwpr9d7gvjwhuzqx59rhw <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
## Summary
- serialize native `openChannel` tray actions with the camelCase field
names consumed by the TypeScript frontend
- prevent a valid tray channel ID from becoming `/channels/undefined`
- add a Rust serialization contract test covering the complete frontend
payload shape
### Root cause
`TrayAction` renamed the enum variant to `openChannel`, but its struct
fields still serialized as `channel_id` and `community_generation`. The
frontend reads `action.channelId`, so tray navigation called
`goChannel(undefined)`.
### Testing
- manually verified the corrected runtime payload and tray navigation
before removing temporary logging
- `just desktop-ci`
- pre-push hooks (desktop checks/tests, Tauri checks, and Rust tests)
---------
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
Co-authored-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz>
Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
## Buzz Desktop release v0.5.5
- **Frozen main:** `383d9e1eafd569b44b9c835200dba69ef7cec9dc`
- **Reviewed candidate:** `ac589061ef1009f55384536e483cfe9b1260697b`
- **Previous desktop release:** `desktop-v0.5.4`
- **Proposed immutable tag:** `desktop-v0.5.5`
This PR must be **squash merged** only after the Desktop Release
Candidate check passes. The branch must remain based directly on current
`main`; stale base, payload drift, incomplete notes, or an unauthorized
merge produce no tag.
The checked-in changelog accounts for every non-merge commit in the
release range. Publication remains bound to the immutable candidate tag.
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Release Automation <release-automation@users.noreply.github.com>
## 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](https://github.com/block/buzz/pull/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>
## Summary
- derive the current desktop package version in the release cache-key
contract test
- mutate that version in both `Cargo.toml` and `Cargo.lock` instead of
assuming `0.5.4`
- prevent desktop release version bumps from failing generic CI
## Context
PR #4788 bumped Desktop to `0.5.5`, exposing the hard-coded fixture. The
dedicated release candidate check passed, while generic CI failed with
`desktop version changed cache key`.
## Verification
- pre-commit hooks passed
- pre-push hooks passed
- CI will validate the full contract
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary
Increases three Playwright assertion timeouts in
`tests/e2e/empty-edit-delete.spec.ts` from 5s to 10s to fix a
shard-composition flake introduced by PR #4694.
## Root Cause
PR #4694 added `huddle-transcription.spec.ts` (477 lines, 22+ tests) to
the Desktop Smoke E2E suite, shifting shard 2 composition so that
`empty-edit-delete` now runs with significantly more accumulated browser
state. The three affected assertions all wait for a React state update
triggered by pressing Enter in edit mode:
- `alertdialog` becoming visible after an empty edit (tests 1 and 2)
- `edit-target` hiding after a successful non-empty edit (test 3)
These transitions go through the React scheduler. In isolation they
complete in milliseconds. In a loaded headless shard with accumulated GC
pressure, the 5s window became insufficient — test 3 failed 3/3 times in
CI run
[30946444168](https://github.com/block/buzz/actions/runs/30946444168)
with `edit-target` still visible after Enter.
No product code is changed. The empty-edit-delete flow is correct and
untouched by #4694. This is a test-environment timing adjustment only.
## What Changed
- `tests/e2e/empty-edit-delete.spec.ts` — three `{ timeout: 5_000 }` →
`{ timeout: 10_000 }` for the post-Enter React-update waits
## Validation
- `just desktop-check` — passed
- `just desktop-test` — 4194 passed, 0 failed
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary
- keep the first-open Buzz Term splash pending until the active PTY
delivers its first frame
- retrigger the splash effect when that readiness gate changes
- cover the real bootstrap path so startup latency cannot consume the
animation invisibly
## Verification
- Wes manually verified the first-open animation in the worktree
- `pnpm --dir desktop typecheck`
- `pnpm --dir desktop test` — 4,195 passed
- `pnpm exec biome check src/features/terminal/TerminalBootstrap.tsx
src/features/terminal/TerminalSubstrate.tsx
src/features/terminal/TerminalBootstrap.test.mjs`
- pre-push hooks — branch skew, desktop check, and 4,195 desktop tests
passed
The repository-wide `pnpm --dir desktop check` still reports
pre-existing diagnostics in `personaCatalogRelay.test.mjs` and
`terminal.css`; the three changed files pass Biome directly.
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary
- remove the fractional half-pixel translation from custom reaction
emoji
- preserve the existing 28px reaction pill, 14×14 glyph box, and
`object-fit: contain`
- add real-app Playwright coverage for integer centering and non-square
intrinsic dimensions
### Related issue
None found. Follow-up to the Buzz emoji-warp investigation.
### Testing
- `cd desktop && pnpm exec playwright test
tests/e2e/custom-emoji.spec.ts --project=smoke` (15 passed)
- `cd desktop && pnpm test` (4,171 passed)
- `cd desktop && pnpm lint` (passed; two pre-existing informational
`useTemplate` diagnostics)
- `cd desktop && pnpm typecheck` (passed)
- `cd desktop && pnpm exec biome check
src/features/messages/ui/MessageReactions.tsx
tests/e2e/custom-emoji.spec.ts` (passed)
Independent review also mutation-tested the regression coverage by
restoring the half-pixel transform and confirming the new test fails. No
after screenshot is included because the patch preserves dimensions and
fixes subpixel raster alignment; the real-app test asserts the mechanism
directly.
Validated at `bc95969b21b58d83b7f94de4ad25e499e52b35fb`.
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Co-authored-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz>
## Summary
- add a visible Stop control for interrupting agent speech
- make push-to-talk available by default while preserving manual mute
controls
- refine agent management, muted audio states, drawer layering, and
return navigation
- suppress duplicate notification sounds for Huddle messages
## Why
Huddles could trap users behind long agent speech, hide useful agent
controls, and leave temporary Huddle state visible after the call. The
drawer also regressed when the terminal substrate began painting behind
the rounded app surface.
## Validation
- `just desktop-ci`
- focused Huddle Playwright coverage for the drawer, speech
interruption, agent picker, and leave navigation
---------
Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Overview
Both local archive settings — "Archive my agents' observer frames" (kind
24200) and "Archive my agents' turn metrics" (kind 44200) — previously
defaulted to OFF in OSS builds, controlled by build-time env vars. This
had an irreversible cost: observer frames are ephemeral (not stored by
the relay), so any missed events are permanently unrecoverable. This PR
makes both settings default to enabled for all builds and removes the
build-time flag machinery entirely.
## What changed
### Rust
- `observer_archive_default_enabled()` — returns `true` unconditionally;
removed `option_env!("BUZZ_DESKTOP_BUILD_OBSERVER_ARCHIVE_DEFAULT")`
check and `nest_is_dev()` runtime fallback.
- `agent_metric_archive_default_enabled()` — returns `true`
unconditionally; removed
`option_env!("BUZZ_DESKTOP_BUILD_AGENT_METRIC_ARCHIVE_DEFAULT")` check
and its OSS-build test.
- `build.rs` — removed both `rerun-if-env-changed` declarations
(`BUZZ_BUILD_OBSERVER_ARCHIVE_DEFAULT`,
`BUZZ_BUILD_AGENT_METRIC_ARCHIVE_DEFAULT`) and the two baked-env
emitting blocks.
### Build / CI
- `Justfile` — removed `desktop-tauri-test-compiled-flags` recipe (the
dual-compile test machinery).
- `.github/workflows/ci.yml` — removed the "Desktop Tauri compiled-flag
verification" CI step.
### TypeScript
- `useObserverArchiveSeed.ts` — removed `observerArchiveDefaultEnabled`
dep from `ObserverArchiveSeedDeps` and the `policyOn` gate in
`reconcileObserverArchive`; the function now unconditionally calls
`mergeSaveSubscriptionKinds`.
- `useAgentMetricArchiveSeed.ts` — removed
`agentMetricArchiveDefaultEnabled` dep from `AgentMetricArchiveSeedDeps`
and the `defaultOn` flag-check path in `maybeSeed`; the
`hasExplicitChoice` guard is preserved as the sole gate against
re-seeding.
- `LocalArchiveSettingsCard.tsx` — removed `policy` prop,
`observerPolicy` state, and `observerArchiveDefaultEnabled` fetch from
`ObserverArchiveSection`; toggle is now always enabled (just `toggling`
disables it); removed the stale "Always on for internal builds" copy
branch; removed the `observerPolicy !== false` guard from
`handleObserverToggle`.
- `tauriArchive.ts` — updated JSDoc on both default-enabled functions to
reflect always-true.
- `e2eBridge.ts` — changed both mock defaults from `?? false` to `??
true` so E2E tests without an explicit mock override exercise the real
default behavior.
### Tests
- `useObserverArchiveSeed.test.mjs` — replaced `policyOn` dep with
direct merge dep; updated `test_oss_policy_off_no_merge` →
`test_reconcile_always_seeds_24200`; all cancellation, identity-switch,
and ordering tests adapted.
- `useAgentMetricArchiveSeed.test.mjs` — removed `defaultOn` dep and
`test_oss_build_does_not_seed`; updated
`test_internal_build_unset_seeds_*` → `test_default_enabled_*`;
`hasExplicitChoice` guard tests unchanged.
## Preservation of explicit opt-outs
Users who have previously toggled the setting off are unaffected:
- `useAgentMetricArchiveSeed` skips seeding when
`hasExplicitChoice(pubkey)` returns true (localStorage-persisted per
identity).
- Observer archive reconciliation now unconditionally calls
`mergeSaveSubscriptionKinds`, but a user who already deleted the
subscription can turn it off via the Settings toggle, which calls
`removeSaveSubscriptionKind` — this is the existing explicit opt-out
path, and the toggle is now always enabled (not locked by a policy
flag).
## Result
- No `BUZZ_BUILD_*_ARCHIVE_DEFAULT` /
`BUZZ_DESKTOP_BUILD_*_ARCHIVE_DEFAULT` references remain outside
CHANGELOG/history.
- Desktop node tests: 4168 pass, 0 fail.
- `just desktop-tauri-check`: clean.
- `just desktop-tauri-test`: all pass.
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary
- stop retrying remote read-state publishes after the local replacement
blob exceeds NIP-44's 65,535-byte plaintext limit
- preserve every local read marker and leave existing relay state
untouched rather than truncating remote state
- keep incoming remote read-state available while suppressing further
invalid publishes for the manager lifetime
## Why
A repaired/reconnecting relay exposed a 1,404-context read-state on iOS.
The app repeatedly serialized and attempted to encrypt that structurally
oversized blob while reconnect catch-up work was running, saturating
Flutter's debug UI isolate and making channel navigation take roughly
ten seconds.
This is intentionally fail-closed and behavior-preserving: local read
behavior continues, but remote publishing pauses until the manager is
recreated. No protocol or persisted-data format changes.
## Verification
- `flutter test` — 1,093 passed, 1 skipped
- `flutter analyze` — no issues
- pre-push `mobile-test` and `branch-skew` hooks passed at
`0b6423c5d4d583194f0bbe69662912133b9ae1ef`
- independent review by Princess Donut: no blocking findings;
compatibility-safe and correctly fail-closed
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
- Import the retry-after bound only in the link preview test module
- Keep production Tauri builds free of the unused-import warning enforced by pre-push clippy
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Classify native preview image outcomes and honor bounded Retry-After cooldowns for rate-limited hosts
- Extract host cooldown bookkeeping to keep the preview command within the desktop file-size guard
- Fetch preview images and favicons independently so secondary failures preserve resolved metadata
- Add bounded frontend concurrency, fragment deduplication, expiring negative cache entries, and cooldown-aware retries
- Render deterministic Compact and Rich fallbacks for transient fetch and browser decode failures
- Expand Rust, metadata-loader, mock bridge, and Playwright coverage for throttling and recovery
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Underline compact preview hostnames and titles together whenever the card is hovered.
- Remove independent hostname hover styling so the interaction consistently represents the full destination URL.
- Restore stronger description colors for standard rich previews and tweet content while keeping hostnames secondary.
- Add E2E coverage confirming both compact identity elements receive the shared hover affordance.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Remove legacy provider-specific compact preview logos and layouts in favor of hostname-driven metadata.
- Present imageless compact previews with the same 84px height, description support, and left-rule treatment as image-backed cards.
- Preserve fetched favicons and make favicon/hostname identity rows link directly to their source URL.
- Align rich and compact hostname and description colors on the same secondary foreground treatment.
- Expand messaging E2E coverage for equal-height cards, generic favicons, linked hostnames, and imageless descriptions.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Expand compact preview thumbnails to an 88px height with slightly wider responsive dimensions.
- Reduce the thumbnail-to-content inset so preview text sits closer to its image.
- Allow compact titles to wrap across two lines for better readability.
- De-emphasize hostnames and descriptions with a softer secondary foreground color.
- Update desktop geometry coverage for the revised dimensions, spacing, and title treatment.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Remove compact card borders and background surfaces while preserving transparent hover behavior.
- Round thumbnail images on every corner and expand cards to the 24rem image-upload width.
- Shorten the preview settings submenu label from Link display to Display.
- Update compact preview geometry coverage for the wider borderless layout and await screenshot animations.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Add a hover-only vertical ellipsis beside the first compact or rich preview.
- Provide a Link display submenu for switching the global Rich or Compact appearance preference.
- Show an actionable confirmation-backed Remove preview option for message authors while keeping display controls available to everyone.
- Link preference-change toasts to Appearance settings and expand rich preview images to match uploaded image width.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Route rich link preview images through the markdown gallery lightbox so preview images support consistent zoom transitions and gallery navigation.
- Extract link preview lightbox behavior and focusable-element discovery into focused markdown modules to keep the main renderer within its size limit.
- Infer image dimensions from loaded thumbnails when explicit lightbox metadata is unavailable.
- Pass the shared lightbox component through the link preview list and attachment layers.
- Render compact preview cards inline with wrapping and place the remove action on the final compact card.
- Preserve rich preview stacking and existing remove behavior for rich layouts.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Render OG description line breaks instead of collapsing them into spaces
- Group blank-line-separated paragraphs with compact spacing to avoid oversized gaps
- Reuse the rich preview image component for consistent skeleton and lightbox behavior across link types
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Update rich preview cards with wider layouts, compact image sizing, and unified content expansion controls
- Allow link titles to span two lines and preserve full expanded descriptions
- Raise fetched description metadata to the standard 280-character X post limit
- Preserve encoded description line breaks while normalizing intra-line whitespace
- Add metadata tests covering line breaks and description length limits
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Replace separate description and image controls with one Show more/Show less toggle that collapses both elements.
- Expand rich preview text to the full available metadata and widen preview containers for readable 280-character tweets.
- Add vertical padding beside the accent border and cap preview images at 300px while preserving responsive sizing.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>