Skip to content

release: merge open PRs for NuncioCrew v0.0.6 - #19

Merged
cursor[bot] merged 102 commits into
mainfrom
cursor/merge-open-prs-6080
Aug 1, 2026
Merged

release: merge open PRs for NuncioCrew v0.0.6#19
cursor[bot] merged 102 commits into
mainfrom
cursor/merge-open-prs-6080

Conversation

@oscarlehuu

Copy link
Copy Markdown

Summary

Merge all currently open PRs into main as the cut for NuncioCrew v0.0.6.

Included:

Conflict resolutions

  1. Presets / Devin avatars (feat: integrate project thread workflow status #9 vs sync: merge upstream Buzz desktop-v0.5.3 #11): keep upstream discovery/presets.rs + bundled devin.svg; drop superseded preset_harnesses.rs remote-avatar path and duplicate devin.png mapping.
  2. Mobile relay session (fix: keep agent activity visible in thread composer and after mobile resume #13 vs fix(mobile): retry live subscriptions after a retryable relay CLOSED #17): keep EOSE replay flush + pause tracking from fix: keep agent activity visible in thread composer and after mobile resume #13, and CLOSED retry timers from fix(mobile): retry live subscriptions after a retryable relay CLOSED #17; unify the controlled test socket on a single messages list.

Release plan

After this lands on main and NuncioCrew Gate is green:

  1. Dispatch NuncioCrew Release with version=v0.0.6, channel=stable, ref=<main SHA>, publish=true (or dry-run first if preferred).
  2. Close the superseded open PRs (feat: integrate project thread workflow status #9, sync: merge upstream Buzz desktop-v0.5.3 #11, fix: keep agent activity visible in thread composer and after mobile resume #13chore(desktop): diagnose silently dropped observer frames #18).

Test plan

  • NuncioCrew Gate green on this PR
  • Desktop / mobile checks that ran for the constituent PRs remain green after conflict resolution
  • Release workflow publishes crew-v0.0.6 and advances stable + dev updater manifests
Open in Web Open in Cursor 

thomaspblock and others added 30 commits July 30, 2026 15:04
## Summary

The Projects overview now lets the page surface flow through its metrics
and activity cards instead of stacking filled panels. Borders and hover
feedback remain, preserving grouping and interaction cues without the
heavy nested background.

### Related issue

None found.

### Testing

- `pnpm exec biome check
src/features/projects/ui/ProjectsOverviewPanel.tsx
src/features/projects/ui/ProjectsActivityFeed.tsx
tests/e2e/project-pr-review.spec.ts`
- `pnpm build:e2e`
- Focused Playwright smoke test: `project overview does not paint a
background behind its cards` (passed)
- Relevant desktop pre-push checks passed; the unrelated integration
gate was blocked by a stale local checksum for migration 25

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
## Context

`buzz users get --name Honey` searches relay-wide profiles and can
return an identically named agent owned by someone else. This caused
agents from the wrong owner to be added to a channel.

## Summary

This bug fix scopes exact-name agent lookup to owner-authored
managed-agent records, then cryptographically verifies each returned
profile's NIP-OA `auth` tag before asserting ownership. The relay and
database contracts remain unchanged.

### Related issue

None found.

## Changes

- Adds `buzz users get --name Honey --owner me|<hex>|<npub>`.
- Resolves `me` to the NIP-OA owner when the CLI runs as an agent,
otherwise to the CLI identity.
- Matches kind `30177` managed-agent record names exactly and
case-insensitively under the requested owner.
- Requires exactly one valid NIP-OA `auth` tag whose verified owner
equals the requested owner and whose `kind` and `created_at` conditions
apply to the profile event before returning `owner_pubkey` or
`owned_by_me: true`.
- Keeps missing, malformed, stale, condition-mismatched, or unverifiable
owner-record candidates visible with `owned_by_me: false` and an
explicit `verification` value.
- Returns every same-name record for the owner so callers can require
explicit selection when duplicates remain.
- Preserves the existing output shape and client-side name filter for
unscoped searches.
- Documents the distinct owner-scoped managed-agent lookup and unscoped
NIP-50 lookup modes.

### Testing

The reviewer-reproducible red and green commands below exercise the
ownership bug against the target branch and this branch.

## Screenshots

Not applicable. This is a CLI-only change.

## Reviewer-reproducible examples

The lookups below were run against the live relay from `main` and this
branch.

### Red: unscoped lookup returns the 100-profile relay-wide cap and
excludes John's agents

On `main`:

```bash
cargo run -q -p buzz-cli -- users get --name Honey \
  | jq '{count: length, first_three: .[:3] | map(.pubkey), johns_agents: map(select(.pubkey == "31b29bcbe69d6716fbb7ba33602b89200bfc9ddfdabcfd1ea6fbfa70b816dfc7" or .pubkey == "4597ac725bba33fc7dd0454c1e2316a5ed770426acf667837d46f6553b3fcf54"))}'
```

Observed output:

```json
{
  "count": 100,
  "first_three": [
    "20d27fc6c0ab4f50b66d1a32a64c5ca1fb985254143ce911f61ab7733333c3d7",
    "00644478cdd9032c563ddc712b3687d8345d948945aab3c18bab95afbf6f519a",
    "93c16697d0e58007bc11fb953208bc6b1cff387b2dee094abc10bf82dfee5424"
  ],
  "johns_agents": []
}
```

`main` also rejects the owner-scoped command:

```bash
cargo run -q -p buzz-cli -- users get --name Honey --owner me
```

```text
error: unexpected argument '--owner' found
Usage: buzz users get --name <NAME>
```

### Green: owner-scoped lookup distinguishes verified and unresolved
records

On this branch:

```bash
cargo run -q -p buzz-cli -- users get --name Honey --owner me \
  | jq 'map({pubkey,display_name,owner_pubkey,owned_by_me,verification})'
```

Observed output:

```json
[
  {
    "pubkey": "0ca77314d7ac8b3fcf6c647cc8cb9c3afd840db3b2a8ff2079f09a168de1827e",
    "display_name": null,
    "owner_pubkey": null,
    "owned_by_me": false,
    "verification": "missing_profile"
  },
  {
    "pubkey": "31b29bcbe69d6716fbb7ba33602b89200bfc9ddfdabcfd1ea6fbfa70b816dfc7",
    "display_name": "Honey",
    "owner_pubkey": "67252b09c31a995daa63aada26569fbc6a3d12f573113f001ce7432f870da820",
    "owned_by_me": true,
    "verification": "verified"
  },
  {
    "pubkey": "4597ac725bba33fc7dd0454c1e2316a5ed770426acf667837d46f6553b3fcf54",
    "display_name": "Honey",
    "owner_pubkey": "67252b09c31a995daa63aada26569fbc6a3d12f573113f001ce7432f870da820",
    "owned_by_me": true,
    "verification": "verified"
  }
]
```

Only the two profiles with valid NIP-OA proofs assert ownership. The
owner-authored record whose profile is absent remains visible but cannot
be selected as verified ownership.

---------

Signed-off-by: npub1qye6rec0htgg3np8yt6plpyyg8cyffaq66emt3kmk05eylckkzhq0hnf2k <0133a1e70fbad088cc2722f41f848441f044a7a0d6b3b5c6dbb3e9927f16b0ae@buzz.block.builderlab.xyz>
Co-authored-by: npub1qye6rec0htgg3np8yt6plpyyg8cyffaq66emt3kmk05eylckkzhq0hnf2k <0133a1e70fbad088cc2722f41f848441f044a7a0d6b3b5c6dbb3e9927f16b0ae@buzz.block.builderlab.xyz>
## Why

Hack-day feedback exposed a dangerous mismatch between the UI and the
underlying access model. The `Anyone` respond-to mode appeared as a
neutral dropdown choice, while a Buzz agent may act with the files,
accounts, and tools available on the machine where it runs.

People reasonably read this as sharing a bot in a channel. The current
UI did not explain that it can also share the agent's available access.

## What

- Reframes `respond-to` as **agent access** in user-facing UI.
- Uses plain audience labels: **Only me**, **Anyone**, and **Selected
people**.
- Warns for **both** sharing modes, not just `Anyone` — `Selected
people` also hands host access to someone other than the owner, so only
the audience phrase differs:
> Anyone can use this agent to access your computer, including files,
accounts, and connected tools.

> Selected people can use this agent to access your computer, including
files, accounts, and connected tools.
- Names the machine the agent actually runs on. A provider-backed
(remote) agent reads:
> Anyone can use this agent to access the server it runs on, including
any accounts and tools available there.

The remote wording deliberately omits the owner's files — those aren't
theirs to describe on a host they don't own.
- Places the warning below the selector for `Anyone`, but **after** the
people picker for `Selected people`, so it never sits between the user
and the selection they came to make.
- Removes Nostr, harness, pubkey, and `!shutdown` jargon from the
primary decision copy. Direct pubkey entry remains available as an
advanced path.
- Replaces the green open-access avatar dot with an amber warning marker
and accessible text. Selected access uses a separate blue status.
- Aligns the sidebar action and profile field with the same language.
- Records the shared-field disclosure contract in
`desktop/src/features/agents/AGENTS.md` so future surfaces do not
silently omit it.

## Design decisions

**Persistent inline warning, not a confirmation modal.** The setting
does not autosave; the consequence remains visible beside the selection
until the person chooses **Save access**. This gives the information
before commitment without adding a dismiss-and-confirm ritual that would
repeat in every create/edit surface.

**An unknown run location falls back to the local wording.** It does not
hedge with "computer or server". A remote host requires an installed
`buzz-backend-*` provider, and without one `WhereToRunSection` never
renders — so "server" would name a concept the owner has never been
shown. When it *is* remote, they picked that host from the selector
themselves. Surfaces never synthesize a run location they don't have.

**One resolution site, published through context.** `AgentDialog`
resolves the run location (`runLocationForBackend` from
`ManagedAgent.backend`, `runLocationForRunOn` from the create flow's
`WhereToRunDraft`) and publishes it via `AgentRunLocationContext`. It is
not threaded as a prop through `AgentDefinitionDialog` (1047 lines) or
`AgentInstanceEditDialog` (1228 lines) — neither uses the value, and
both are already over the file-size ceiling. Surfaces outside that tree
(`EditRespondToDialog`) pass the prop directly.

The copy follows the writing system's guidance for high-sensitivity
decisions: lead with the material consequence, use plain actor/action
language, keep helper text adjacent and persistent, and never rely on
color alone.

## Scope

Desktop only. The web and mobile clients do not currently expose this
setting. No protocol, gate, runtime, persistence, or backend behavior
changes.

This does not add team-scoped remote agents. It makes the current
local-or-remote access model honest while that product work remains
separate.

## Validation

- `pnpm exec biome check` and `pnpm exec tsc --noEmit` — clean
- `lib/agentAccessWarning.test.mjs` (8/8) — every mode × run-location
copy variant, both resolvers, unknown-reads-as-local, blank `runOn` is
not a provider
- `ui/respondToFieldContract.test.mjs` (8/8) — plain labels, both
warning positions, source-order guard that the `allowlist` warning
follows the picker, helper-not-inline-copy guard
- `agent-access-warning.spec.ts` (3/3) — native local, provider-backed
remote (asserts the server sentence and *not* "your computer"),
persona-backed edit; includes a bounding-box check that the `Selected
people` warning renders below the picker

---------

Signed-off-by: David Hamilton <daveh@squareup.com>
Signed-off-by: Clay Delk <clay.delk@gmail.com>
Co-authored-by: Clay Delk <clay.delk@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary

- restore direct community member adds in **Settings → Invites**
- keep one consolidated entry point: the dialog now supports both adding
someone directly and sharing an invite link
- accept npub or 64-character hex keys while preserving role hierarchy
(owners: Member/Admin; admins: Member only)
- verify an npub direct-add publishes the decoded hex key in a kind
`9030` NIP-IA event

## Why

The Invites consolidation left `AddMemberDialog` without a live mount
point, so the existing direct-add capability disappeared even though its
mutation path still existed. This reuses that implementation rather than
introducing a second one.

## Before and after

| Before | After |
| --- | --- |
| The consolidated dialog only offered a share link; there was no
direct-add path. | The same dialog now presents direct add and
share-link controls as one invitation flow. |
| ![Before: invite dialog with share-link controls
only](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3634/before-invite-dialog.png)
| ![After: polished community invite dialog with direct-add and
share-link
sections](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3634/invite-dialog-polished.png)
|

<details>
<summary>Before: Invites page entry point</summary>

![Before: Invites settings
page](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3634/before-invites-page.png)

</details>

## Verification

- `pnpm --dir desktop build:e2e`
- `pnpm exec playwright test
tests/e2e/invites-settings-screenshots.spec.ts --project=smoke` — 4
passed
- targeted Biome check on modified files
- push hook: Desktop check and 3,783 Desktop tests passed
- GitHub CI green except Desktop E2E Relay still running at last status
snapshot

---------

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: kenny lopez <klopez4212@gmail.com>
Co-authored-by: Joah Gerstenberg <joah@squareup.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Co-authored-by: kenny lopez <klopez4212@gmail.com>
Brings the Flutter app's emoji and thread surfaces up to desktop parity.

## Emoji

- **Full emoji-mart dataset** generated from the same `@emoji-mart/data`
set desktop uses (1,870 emoji, 8 categories), committed as an asset — so
shortcodes, names, and keywords are identical across clients. `just
mobile-emoji-data` regenerates it.
- **Rebuilt the tray**: search (a Dart port of desktop's tiered
`emojiSearch` ranking, extended to names and keywords), a
frequently-used section, and one continuous scroll with pinned section
headers. The category rail is a shortcut into that list, not a page
switcher, and spans the full width the search field uses. Custom emoji
share the native glyph size and cell.
- **Reaction pills** match desktop's geometry, and the count shows at 1.
- **Emoji-only messages** render at 36px with 1.45em inline custom
emoji, matching desktop's `emojiOnly` treatment.
- **Positive-emoji burst** ported from desktop's `EmojiBurstProvider`,
suppressed under reduced motion.

## Threads

- **Top-down layout** — head first under the app bar, replies flowing
down, like desktop's thread panel. The old reversed list bottom-anchored
the content and jammed the head against the composer.
- **Tap a channel message to open its thread**; long-press still opens
the action sheet.
- **Live reactions.** The thread's relay query is one-shot and its
`kinds` filter carries only content rows, so a reaction event could not
reach an open thread at all, and `allMessages` was a snapshot frozen
when the route was pushed — a new pill only appeared after leaving and
re-entering, which refetched. The live channel events are now unioned
into the thread's list. The burst is also route-guarded, since the
channel timeline stays mounted underneath and was claiming it first.
- The `+` affordance follows the channel: replies stay bare until they
carry a reaction, and the head keeps a standing `+`.

## Keyboard

A deliberate downward drag past ~48px dismisses the keyboard; short
scrolls leave it alone. Applies to the channel list, the thread list,
and the compose bar (via a raw `Listener`, so it can't steal the field's
tap or selection drags).

True finger-tracking dismissal is out of scope — Flutter only offers
`manual`/`onDrag`, and 1:1 tracking needs a native `UIScrollView` proxy
plus Android's `WindowInsetsAnimationController`.

## Testing

`just mobile-check` and `just mobile-test` pass (965 tests). New
coverage for emoji search ranking, dataset parsing, the emoji-only
predicate, tray scroll/rail behavior, reaction pills and the burst, and
both thread fixes above. `just ci` green.

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: npub12zsjdqx8dud99s9h47xmk9lq93vryf7zjrae8wdrmma52cg5yglseyulst <50a12680c76f1a52c0b7af8dbb17e02c583227c290fb93b9a3defb456114223f@buzz.block.builderlab.xyz>
Signed-off-by: klopez4212 <klopez4212@gmail.com>
Co-authored-by: npub12zsjdqx8dud99s9h47xmk9lq93vryf7zjrae8wdrmma52cg5yglseyulst <50a12680c76f1a52c0b7af8dbb17e02c583227c290fb93b9a3defb456114223f@buzz.block.builderlab.xyz>
## Summary

- let the Projects page surface flow through repository, pull request,
and issue list/grid layouts
- remove opaque fills from project-detail content across Overview,
Files, Commits, Issues, Pull Requests, and Contributors
- preserve borders, hover feedback, and intentional nested fills for
code, inputs, badges, and warnings

## Related

Follow-up to block#3416.

## Testing

- `pnpm exec biome check` on the changed Projects UI and E2E files
- `pnpm build:e2e`
- focused Playwright smoke coverage for overview, subsection list/grid,
and project-detail transparency (3 passed)
- pre-commit checks passed
- desktop pre-push checks passed; the unrelated integration hook remains
blocked by a stale local checksum for migration 25

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
## Summary

- Keep `Sending…` beside message timestamps, including grouped pending
messages.
- Match the profile-card hover surface to inactive channel rows.

## Snapshots

### Pending message

![Pending message
status](https://raw.githubusercontent.com/block/buzz/655189aec0904677a68651f8aabf0e17d69734cd/pr-3543--pending-message-inline.png)

### Profile hover

![Profile
hover](https://raw.githubusercontent.com/block/buzz/655189aec0904677a68651f8aabf0e17d69734cd/pr-3543--profile-hover.png)

## Validation

- `pnpm -C desktop typecheck`
- `pnpm -C desktop check:file-sizes`
- `pnpm -C desktop build:e2e`
- `pnpm -C desktop exec playwright test
tests/e2e/message-feedback-snapshots.spec.ts --project=smoke`

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Summary

- Add a **macOS-only** monochrome Buzz menu-bar icon with **Running**
and **Recent** agent sections.
- Show each agent as one selectable macOS row with `Name · elapsed` and
its channel underneath.
- Keep completed work available for quick channel re-entry, alongside
New Channel, Open Buzz, and Quit Buzz.
- Keep the main window alive on close and restore it from both the menu
bar and the macOS Dock.
- Fence queued channel actions by community generation so an in-flight
action from the previous community cannot navigate the newly selected
community.
- Leave Windows and Linux unchanged; cross-platform tray lifecycle
support can follow with platform-specific validation.

<img width="812" height="760" alt="Buzz macOS agent menu"
src="https://github.com/user-attachments/assets/19bfd874-8f06-4496-bdda-7ed2e7b5733f"
/>

## Validation

- `cargo fmt --check`
- Desktop Tauri suite: 1,860 passed, 14 ignored after merging current
`main`
- Desktop tests: 3,769 passed
- Desktop lint, file-size, text, and pubkey checks pass (two
pre-existing informational template-literal notices)
- Regression coverage verifies stale `OpenChannel` actions are discarded
across community changes while `NewChannel` survives

## Manual validation remaining

A native macOS smoke test is still requested before merge: menu
appearance, elapsed updates, Running → Recent, channel navigation, New
Channel, minimized/closed/Dock restore, Open Buzz, and Quit. E2E stubs
the tray IPC and does not exercise the native menu.

---------

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>
## Summary
- Keep the Agents header full width while cards reflow independently.
- Collapse header actions into an overflow menu at the compact layout
threshold.
- Apply the same responsive grid rules to Agent Teams.

## Validation
- `pnpm -C desktop build:e2e`
- Focused Agents Playwright coverage
- Pre-push desktop checks and unit tests

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Microphone/camera capture works on macOS (WKWebView) and Windows
(WebView2) but fails on Linux with `NotAllowedError`. WebKitGTK ships
with `enable-media-stream` off and a default `permission-request`
handler that denies every request.

This reaches the underlying `webkit2gtk::WebView` from
`on_webview_ready` and enables `enable-media-stream`, then installs a
**deny-by-default** `permission-request` handler: a `UserMedia` request
is allowed only from a trusted app origin (`tauri://localhost` in prod,
the Vite dev origin in debug) **and** when it targets an audio/video
device — everything else is denied. No-op on macOS/Windows.

- `webkit2gtk` is pinned to the version wry already uses (`=2.0.2`) so
there's a single shared copy of the native binding.

---------

Signed-off-by: Beckley <mattcbeckley@gmail.com>
## Summary

- Refine the agent share dialog around recipient sharing, link copying,
catalog sharing, and export.
- Show memory settings only when a linked agent has memories to include.
- Use a catalog toggle for custom agents and keep built-in agents out of
the catalog flow.

## Validation

- `pnpm typecheck`
- Focused Playwright share and catalog flows

---------

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>
Buzz renders one card per `kind:30617`, so a project spanning several
repositories has no representation — the relay, desktop app, and mobile
app look like three unrelated things. This adds the spec for the
container event that fixes that, plus the two shared fixture files that
make it machine-checkable. Docs only; no code changes.

Membership cannot live in the repository announcements themselves. A
project spanning Alice's and Bob's repositories would need *both* of
them to publish a tag naming the group, and Alice cannot sign for Bob's
key. A project's own name, description, and channel binding likewise
have no single writer when scattered across per-repository tags, and no
deletion story. That is why multi-repo grouping is the one forge concept
in Buzz that warrants a custom kind.

## `docs/nips/NIP-MP.md`

`kind:30621`, an addressable event per NIP-01, addressed by `(pubkey,
30621, d)`. Members are `a` tags holding canonical
`30617:<lowercase-64-hex-owner>:<repo-d>` coordinates, following
NIP-01's 2-or-3-element grammar where the optional third element is a
relay hint clients MAY use and whose content ingest does not parse.
Metadata is `name`, `description`, `buzz-channel`, `buzz-visibility`.

- **Authority stops at the container.** The signer can replace their own
project and nothing else — no edit, delete, push, or admin over any
member. Deletion additionally admits the signer's registered NIP-OA
owner, because `validate_standard_deletion_event`
(`crates/buzz-relay/src/handlers/side_effects.rs`) grants that
platform-wide so a human can clean up events published by an agent they
own; the spec documents it as a Buzz extension to NIP-09 rather than
carving `kind:30621` out of it. `buzz-channel` on a project is metadata
only; git push policy reads the repository's own `kind:30617`
(`crates/buzz-relay/src/api/git/policy.rs`) and a project never becomes
an input to it.
- **Ingest validation contract**, with named rules the fixtures
reference: `d-cardinality`, `d-empty`, `member-cap` (64, counting every
`a` tag), `member-tag-arity`, `member-coordinate-malformed`,
`member-duplicate`, `metadata-cardinality`, `metadata-length`. Arity is
its own rule rather than part of coordinate parsing, because a
four-element member tag can carry a valid coordinate — the tag's shape
is what is wrong, and ignoring elements past the relay hint would admit
unvalidated data no consumer reads. Duplicates are rejected rather than
normalized — a relay cannot rewrite tags inside a signed event without
invalidating its id and signature.
- **Metadata interpretation is normative, not left to the reader.**
Ingest bounds cardinality and length and interprets nothing; clients
resolve absent `name` to the `d` value, any unrecognized
`buzz-visibility` token to `listed` (a typo is not a privacy signal),
and an unresolvable `buzz-channel` to a project rendered without a
channel rather than dropped. `content` carries no meaning: writers
SHOULD emit `""`, and readers and relays MUST ignore any value rather
than reject it.
- **Claim authority.** A project suppresses a member's standalone card
only when it is listing eligible *and* its signer is that repository's
owner or appears in the repository's own `maintainers` tag. Without
this, anyone could publish a project naming your repository and pull it
out of the collection into a container you never consented to. An
unauthorized project still renders, and still renders its members — it
just cannot remove a repository from where its owner expects to find it.
- **Deterministic client fold**, seven steps, with a table of required
cases: exhaustive enumeration (a fixed `limit: 200` makes repository 201
vanish), multiple membership, fallback to a standalone card,
unresolvable members marked unavailable rather than dropped, and local
hide of a container never hiding repositories. On a relay that provides
no exhaustive mode, the conformant behavior is a persistently marked
possibly-incomplete collection — not a violation of the enumeration
requirement.
- **Pagination is specified in two modes**, because exhaustive
enumeration is not universally achievable. Both modes share an explicit
three-condition relay contract: a relay must (1) apply the complete
filter before enforcing any limit, (2) expose the exact effective page
limit it enforces, and (3) saturate pages — return `min(effective limit,
remaining matches)`, so a short page proves all remaining matches were
returned. A relay satisfying any proper subset does not provide the
guarantee, and absent it a client MUST mark the collection possibly
incomplete. On a relay exposing a composite `(created_at, event id)`
keyset cursor — Buzz does on its authenticated HTTP bridge endpoint, via
`until` + `before_id`; the NIP-01 websocket REQ path silently discards
`before_id`, so a websocket client against Buzz is in mode 2 — clients
MUST page by it; within the relay contract the cursor's uniqueness means
no skips or re-reads and a short page is an unambiguous end signal, but
cursor uniqueness alone does not substitute for the relay contract. A
vanilla NIP-01 filter has no id tiebreak, so `until` alone either skips
a second's unread events or never advances; there a client MUST drain
the boundary second explicitly. The spec also adds normative guidance on
query shapes: a client MUST use only query shapes the relay applies
completely before limiting, and where a needed constraint (such as `#a`)
is post-applied, MUST widen to a pushable shape and match the rest
client-side.
- **Kind allocation** recorded with the checks performed: `30621` is
unassigned in the upstream nostr NIPs kind table and has no
nostrbook.dev entry, and it is the one free number between `30620` and
`30622` locally.

## `docs/nips/NIP-MP.fixtures.json`

The ingest contract: 31 cases — 11 accept, 20 reject — as unsigned
templates consumers sign with their own test key. Coverage includes
minimal and full projects, zero members, the 64-member boundary from
both sides, cross-owner and same-`d`-different-owner members,
colon-bearing repository `d` values, relay hints, non-empty `content`,
and every rejection rule. Each of the two 256-byte `buzz-` bounds gets
its own reject case so neither can hide behind the other's rejection,
and duplicate detection is pinned to the coordinate alone by a case
whose two identical coordinates carry different relay hints. A
four-element member tag carrying an otherwise valid coordinate pins
arity separately from coordinate parsing. Every rejection case names the
rules that may fire, so an implementation cannot pass by rejecting a bad
event for an unrelated reason.

## `docs/nips/NIP-MP.fold-fixtures.json`

The fold oracle: 12 cases covering every row of the required-fold-cases
table, including the discriminating case where one authorized and one
unauthorized project list the same repository — an implementation that
requires every listing project to be authorized emits a spurious
implicit card, and one that lets any listing project suppress drops a
card it owes the owner.

Inputs are semantic rather than signed envelopes: a repository or
project is named by its coordinate plus only what the fold reads —
signer, members, `maintainers`, visibility, viewer-hidden, deletion.
Every collection in `expect` is compared as a set, including each
container's `members`, since the fold fixes placement and not order.
Signing would re-test the ingest contract and obscure what is under
test. The fold is where claim authority lives, so without a shared
oracle two clients could each satisfy the prose and still render
different collections from identical heads.

## `VISION_PROJECTS.md`

Line 41's "zero custom kinds" now reads "no custom kind for the repo
itself", with a new "One Project, Many Repos" section recording why the
one exception is warranted. `30621` rows added to the kind and status
tables.

Related: block#3171 (the `KIND_PROJECT` constant, relay ingest validation of
this contract, and the inclusive `created_at <= tombstone` bound this
spec's coordinate-deletion rule cites). Independent — either can merge
first.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…(split 1/2 of block#3467) (block#3741)

## Summary

This is **part 1 of 2** split out from block#3467 (per Tyler's request),
carrying only the mesh-scoped changes. The agent/ACP response-behavior
changes and the new `send_message` tool stay in block#3467 as part 2. All
commits are @michaelneale's work, cherry-picked with authorship
preserved.

- Upgrade embedded Mesh to v0.74.0 (tag-pinned instead of commit rev)
and use canonical Gemma model IDs.
- Keep shared compute serving through member joins, roster changes, app
recovery, and community switching.
- Wait for actual model readiness and avoid resuming incomplete
downloads after quit.
- Leave `BUZZ_AGENT_THINKING_EFFORT` unset by default so each model's
chat template picks its own thinking default (`none` suppressed Gemma
tool-calling entirely; pinning `low` made Qwen3 burn ~4x output budget).
Explicit agent/persona/global values still win.

## Relationship to block#3467

Contains the mesh commits from block#3467 (`2cd640b23`, `0ad81c341`,
`ad13ed841`) rebased onto current main, with one deliberate exclusion:
the `crates/buzz-agent/src/llm.rs` reasoning→text parser change from
`2cd640b23` is **not** here. That change unconditionally affects every
OpenAI-compat/Responses provider, so it belongs with the reply-behavior
work in part 2, where it can be reviewed as what it is.

Not included (remaining in block#3467 / part 2):
- typed `send_message` tool in dev-mcp + `BUZZ_ACP_SEND_MESSAGE_TOOL`
gating
- plain-reply delivery fallback in buzz-acp
(`BUZZ_ACP_DELIVER_PLAIN_REPLIES`)
- the mesh_agent_e2e P5/P6 rewrite (exists to prove the reply path)
- the two `env.insert` preset opt-ins in `relay_mesh.rs` for the flags
above
- the llm.rs parser change

This PR is independently mergeable; part 2's flags are all off by
default so it can land before or after.

## Testing

- `cargo test -p buzz-relay --locked` — 780 passed (one telemetry test
is order-sensitive under parallel default settings; passes in the
pre-push suite and standalone, unrelated to this diff — files untouched
here).
- `just desktop-tauri-test` (default features) — 1877 passed.
- `cargo test --locked --features mesh-llm` in `desktop/src-tauri` —
1961 passed, including the new relay-mesh preset and
coordinator/recovery tests.
- Both `Cargo.lock`s resolve with `--locked` against the v0.74.0 tag.
- Full pre-push hook suite green (rust-tests, desktop-check/test, tauri
checks).

Live validation of the mesh v0.74 upgrade itself is documented on block#3467
(two-Mac cross-version test).

---------

Signed-off-by: Michael Neale <michael.neale@gmail.com>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
…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>
## Why
Long Buzz threads were rendered as `[Thread Context (13 of 13
messages)]` because the harness counted only the already-limited query
result. That hid older context and could also hide the agent's own prior
reply in busy threads.

## What
- Fetch one extra thread reply as a sentinel so truncated context is
labeled correctly.
- Use a best-effort `/count` call for improved truncated totals when
available, clamped to the sentinel-proven minimum so racy counts cannot
render impossible labels.
- Keep the `/count` path single-attempt with a short timeout and only
add the root to exact totals when the root was actually fetched.
- Fetch and preserve the agent's newest prior reply when it falls
outside the recent window, with exact event-id matching for the
pin/dedup boundary.
- Add parser and fetch-boundary tests for truncation, exact count,
missing root, count-below-minimum clamping, count failure fallback,
distinct fetched-reply lower bounds, agent-reply dedup/pinning, and
serialized query/count filter semantics.

## Risk Assessment
Low-to-medium — limited to buzz-acp prompt context fetching and a small
RestClient helper. If `/count` fails or times out, the code falls back
to the sentinel-derived minimum total rather than failing the prompt.
The synchronous `/count` happens only for truncated thread contexts and
is bounded to one short best-effort attempt.

## References
- Buzz thread: chotchkies-buzz-bombing-flakes /
`7ef71407f1c7a642382c7e48e0c80fb6ca66948890e04d1eb6f1408c3b7278b1`
- Validation at `c1cfd1b16a04a3ac1d1d0d3cf43e1a08508f3532`:
  - `cargo fmt -p buzz-acp` ✅
- `cargo test -p buzz-acp test_fetch_thread_context -- --nocapture` ✅ (6
tests)
  - `cargo test -p buzz-acp parse_nostr_thread_response` ✅
  - `cargo test -p buzz-acp` ✅ (649 unit + 9 lifecycle tests)
  - `git diff --check` ✅
- Push was completed with `--no-verify` after pre-push hooks reached
non-code local environment failures: `flutter` missing for
`mobile-test`; Node.js v20.20.2 too old for pnpm/node:sqlite in
`desktop-check` and `desktop-test`. Earlier hook stages passed:
`check-push-org`, `branch-skew`, `rust-tests`, `test`,
`desktop-tauri-checks`.
- Earlier full `./bin/just ci` at
`622ed7eb8807d64e06209101569b1013414af091` ⚠️ passed Rust/desktop/web
stages, then failed in `mobile-test` on unrelated existing mobile test
`ChannelDetailPage keeps follow mode off while a tall newest message
stays visible`; rerunning that single mobile test reproduced the same
failure without touching mobile code.

Generated with Codex

Signed-off-by: npub1m0vvn9qm5md0a080p27qzkm9uaw49e699ukwfq7fc0756xq0y5zqhzhdk2 <dbd8c9941ba6dafebcef0abc015b65e75d52e7452f2ce483c9c3fd4d180f2504@buzz.block.builderlab.xyz>
Co-authored-by: npub1m0vvn9qm5md0a080p27qzkm9uaw49e699ukwfq7fc0756xq0y5zqhzhdk2 <dbd8c9941ba6dafebcef0abc015b65e75d52e7452f2ce483c9c3fd4d180f2504@buzz.block.builderlab.xyz>
## Summary
- replace Amp's outdated Sourcegraph attribution in the runtime catalog
- describe Amp neutrally as a coding agent for the terminal and editor

## Verification
- `pnpm test` (desktop: 3,819 passed)
- `pnpm typecheck`
- pre-push `desktop-check`, `desktop-test`, and `branch-skew` hooks

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary

- Render selected agent mentions as visible bot chips in the mobile
composer.
- Recognize agent profiles consistently when rendering message-body
mentions.
<img width="630" height="1368" alt="Screenshot 2026-07-30 at 07 54 16"
src="https://github.com/user-attachments/assets/035b46bf-ee78-4ee5-82fc-84591415ed7c"
/>

## Validation

- `flutter test test/features/channels/compose_bar_test.dart
test/features/channels/message_content_test.dart`
- `flutter analyze`

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Why
People joining a community with an existing relay profile should not be
asked to recreate their name and avatar.

## What
- Check the active identity's relay profile after the joined community
becomes active
- Skip directly to the starter-team step when a kind-0 profile event
exists
- Preserve the profile setup path when no event exists or discovery
fails
- Cover both new-profile and existing-profile join paths in E2E tests

## Risk Assessment
Low — the lookup is scoped to the community onboarding profile stage,
runs once per transaction, and fails open to the existing flow.

## References
- `pnpm build:e2e && pnpm exec playwright test --project=integration
tests/e2e/onboarding.spec.ts --grep 'first-community direct join reaches
profile|community onboarding reuses an existing relay profile'` (2
passed)

Generated with Codex

Signed-off-by: npub1rf6fvdj6ut0c4kcmjv4p5mmgh89nj58n69uu3fz3cvk3jn500hqs7emz79 <1a7496365ae2df8adb1b932a1a6f68b9cb3950f3d179c8a451c32d194e8f7dc1@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1rf6fvdj6ut0c4kcmjv4p5mmgh89nj58n69uu3fz3cvk3jn500hqs7emz79 <1a7496365ae2df8adb1b932a1a6f68b9cb3950f3d179c8a451c32d194e8f7dc1@sprout-oss.stage.blox.sqprod.co>
**Category:** new-feature
**User Impact:** Users can create, download, and verify a
password-protected backup of their private identity from desktop
Settings.

**Problem:** Buzz does not currently give signed-in users a
Settings-based path to protect or validate their private identity
independently of onboarding. **Solution:** Add a focused backup menu to
the private-key row, keep encryption and verification local in Rust, and
preserve completed encrypted backups briefly so native saves can be
retried without repeating encryption.

<details>
<summary>File changes</summary>

**desktop/src/features/settings/**
Adds the background backup lifecycle, create and test dialogs,
private-key menu integration, password handling, and focused unit
coverage.

**desktop/src/features/onboarding/ui/NsecMaskedDisplay.tsx**
Extends the masked private-key display with reusable overflow-menu
actions used by Settings.

**desktop/src/app/App.tsx**
Mounts the backup provider at app scope so encryption and save work
survive closing Settings or the modal.

**desktop/src/shared/api/tauriIdentity.ts**
Adds typed desktop bindings for local backup creation, save, selection,
and verification.

**desktop/src-tauri/src/key_backup.rs and
desktop/src-tauri/src/commands/identity.rs**
Implements local NIP-49 encryption, password generation, file handling,
and public-identity-only verification results.

**desktop/src-tauri/src/egress_guard.rs and guarded call sites**
Blocks encrypted secret material from relay, websocket, snapshot,
sharing, and huddle egress paths.

**desktop/src-tauri tests and fixtures**
Covers encryption, verification, file behavior, and fail-closed
no-egress protections.

**desktop/src/testing/e2eBridge.ts, desktop/tests/, and
desktop/playwright.config.ts**
Expands the mock native bridge and browser coverage across create,
retry, expiry, and current/different-identity verification states.

**desktop/src-tauri/Cargo.toml, Cargo.lock, and assets**
Adds the local cryptography/password-generation dependencies and
embedded short-word list.

</details>

## Reproduction steps

1. Run the desktop app and open **Settings → Profile → Identity**.
2. Open the private-key overflow menu and choose **Create backup**.
3. Enter or generate a valid password, submit, and confirm progress
continues if the dialog or Settings is closed.
4. Save the resulting `.ncryptsec` file; cancel and retry to confirm the
temporary download remains available.
5. Choose **Test backup**, select the file, enter a wrong password, then
retry with the correct password.
6. Confirm success identifies whether the backup matches the current
identity and displays only the public `npub`.

## Screenshots

| Settings identity | Private-key menu | Create backup |
|---|---|---|
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/981e391b-6829-4081-95ca-ca75a369de71"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/7972c68e-7635-47d8-b0ad-9639390d3e6c"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/4709c8f7-cf02-46f1-bec9-b3f98fe56fb2"
/> |

| Encrypting | Download available | Test success |
|---|---|---|
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/1ac3e934-2b4b-4135-bae6-126c715c8c59"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/cb6f07ee-a16f-44a5-b9a0-6b9fe0e4d40d"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/ea58b1b1-966c-46aa-8d59-92c9f06a25bd"
/> |

Visual review and additional states: [Buzz
thread](buzz://message?channel=50ca7ef1-201e-4159-9499-40de3964b7c3&id=87eceb5f0f82fd50c32e560de3d35be48e293760f6620718aafdcef289d475fe)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
## Summary

- make the relay reconnect coordinator authoritative during outages so
query, publish, and subscription traffic waits for the scheduled attempt
instead of cancelling backoff
- release waiting operations after the coordinated AUTH +
live-subscription replay attempt, while preserving one explicit manual
reconnect fast path
- suppress duplicate notification side effects when reconnect replay
overlaps previously delivered events

## Root cause

`resetConnection()` scheduled exponential backoff, but
`ensureConnected()` cleared any pending reconnect timer. Operation-level
retry paths immediately called `ensureConnected()`, so ordinary app
traffic could repeatedly bypass the reconnect policy during an outage.
The resulting churn also replayed overlapping live events into
notification side effects without a shared event-ID guard.

## Validation

- `pnpm --dir desktop typecheck`
- `pnpm --dir desktop test` — 3,823 passed
- pre-push: `desktop-check`, `desktop-test`, and `branch-skew` passed
- file-size, px-text, and pubkey-truncation ratchets passed

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary
- add a manual desktop release preparer that regenerates one
version-only candidate from current `origin/main`
- validate deterministic complete changelog accounting, candidate
authorship, allowed files, exact-head approval, required checks, and
two-parent merge topology before tagging the reviewed candidate
- move desktop tags/releases from `v*` to `desktop-v*` while preserving
relay, chart, push-chart, and mobile behavior
- stage all four platform outputs in Actions artifacts and grant GitHub
release write access only to one final all-platform-gated publisher
- publish the versioned release only after complete artifact assembly;
update stable `latest.json` last; never promote prereleases or published
rebuild outputs

## Safety properties
- desktop tags point to the reviewed candidate SHA, not the merge commit
- release builds remain tag-bound and reverify tag == checked-out HEAD
- one final writer fails closed on artifact basename collisions
- per-tag concurrency serializes publication without cancellation
- published reruns do not replace immutable versioned assets or promote
signatures from a rebuild
- candidate branches use an explicit remote OID lease when regenerated

## Validation
- `scripts/test-desktop-release-candidate.sh`
- `scripts/test-release-ref-contract.sh`
- `scripts/test-mobile-release-contract.sh`
- changed workflow YAML parsing (Ruby Psych)
- changed shell syntax (`bash -n`)
- `git diff --check`
- push hooks: branch-skew, Rust workspace tests (1,853 passed), desktop
Tauri tests (3 passed)

## Coordinated companion
- squareup/buzz-releases#79 updates the manually entered desktop
source-tag contract to stable-only `desktop-v*`
- merge the private contract companion before the first namespaced
desktop release

## Rollout blockers (no settings changed here)
Before the first candidate/release:
1. enable merge commits in repository settings
2. allow `merge` in ruleset `13596885`
3. require approval after the last push in ruleset `13596885`
4. include `refs/tags/desktop-v*` explicitly in release ruleset
`14378754`
5. prove the non-publishing candidate/merge/tag/artifact validation path
before any production release

Do not test the old workflow with a prerelease: it can still mutate the
production rolling updater release.

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary
- Show video review comments when a video is opened from a thread reply.
- Reuse review-context construction across timeline and thread views.

## Validation
- `pnpm run build:e2e && pnpm exec playwright test
tests/e2e/video-attachment.spec.ts --project smoke --grep "video replies
in threads open the review comments view"`
- `pnpm test`

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Summary

- use uniform 4px top and bottom padding for continuation rows
- keep continuation timestamps top-aligned and remove the thread-only
minimum-height gutter
- raise continuation hover actions by 12px
- align virtualized row estimates with the compact layout

## Validation

- `pnpm test` (3,782 tests via pre-push)
- `pnpm check`
- desktop snapshots

## Screenshots

### Mention-chip continuation

![Mention-chip
continuation](https://raw.githubusercontent.com/block/buzz/85b88763ef8147f3376c9bf794bc0973a0211a57/pr-3724--thread-continuation.png)

### Emoji continuation

![Emoji
continuation](https://raw.githubusercontent.com/block/buzz/85b88763ef8147f3376c9bf794bc0973a0211a57/pr-3724--channel-continuation.png)

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Summary

- send desktop presence heartbeats every 60 seconds instead of every 30
seconds
- extend presence TTL from 90 to 180 seconds to preserve the existing
three-heartbeat expiry window
- add mutation-sensitive tests that pin the one-minute / three-window
timing contract
- update presence documentation to match

This halves steady-state **desktop** presence `SET` + `PUBLISH` traffic
while retaining tolerance for two missed heartbeats. Mobile already uses
a 60-second heartbeat, so the fleet-wide reduction depends on desktop's
share of connected clients.

## Rollout order

Deploy the relay TTL increase before shipping the desktop heartbeat
change. Old desktop + new relay is safe; new desktop + old relay leaves
only a 90-second TTL on a 60-second cadence and can flap after one
missed heartbeat.

## Verification

At initial live-test commit `00816e233b187bc5ba12c667d675ed050a8cc1c9`:

- isolated clean-room relay built from the exact SHA against fresh
Postgres, Redis, and MinIO
- live Redis `MONITOR` observed kind-20001 writes as `SET ... EX 180`,
global `PUBLISH`, and clean-disconnect / explicit-offline `DEL`
- normal workflows passed: channel create/update/archive/unarchive;
message send/get/reply/thread/search; archived-channel write rejection
and resumed write after unarchive

At follow-up commit `bf38a8c5c96f196ff8ee46e48d4141ee7811f186`:

- `pnpm -C desktop test` — 3829 passed
- `pnpm -C desktop typecheck`
- `cargo test -p buzz-pubsub` — 24 passed, 11 Redis-dependent tests
ignored
- mutation probes fail when the server TTL changes to `999999` or the
desktop heartbeat changes back to 30 seconds
- `git diff --check`

The pre-push suite's relevant checks passed, but its unrelated Tauri
clippy step fails on current `origin/main`:
`desktop/src-tauri/src/linux_media.rs` has three dead-code warnings on
macOS. This PR does not modify that file, so the branch was pushed after
independently running the suites above.

## Buzz context

Originating channel: `buzz-redis-cluster-mode`
(`f4e36d32-afdb-447f-8c87-ab003e069d18`)

---------

Signed-off-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
**Category:** improvement
**User Impact:** Activity feeds now clearly identify the agent and keep
update recency visible even when channel names are long.

**Problem:** The activity header led with a generic label, making it
hard to tell which agent was in view, while channel scope and recency
competed for limited horizontal space. Long channel names could hide the
update timestamp entirely.

**Solution:** Lead with the resolved agent avatar and name, then place
mode and scope in a truncating metadata region with recency pinned at
the right edge. This preserves the compact two-line header while keeping
the most important identity and freshness signals legible.

<details>
<summary>File changes</summary>

**desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx**
Reorganizes the activity header around the agent identity, reuses the
existing resolved profile avatar and label helpers, and separates scope
truncation from the always-visible recency label.

**desktop/tests/e2e/activity-scope-label-screenshots.spec.ts**
Expands activity-header coverage across channel-scoped, all-channel,
raw, long-name, and narrow layouts, including measured truncation and
recency visibility.

</details>

## Reproduction steps

1. Open an agent's activity feed from a channel.
2. Confirm the agent avatar and name lead the header.
3. Open a feed scoped to a channel with a long name and resize the panel
narrowly.
4. Confirm the mode and channel scope truncate while the recency label
remains visible at the right edge.
5. Toggle Raw mode and open an all-channel feed to confirm the same
hierarchy and truncation behavior.

## Screenshots

| Long channel | Narrow layout |
|---|---|
| <img width="380" height="671" alt="image"
src="https://github.com/user-attachments/assets/19682aac-9938-41ed-8c27-fe59bf8b7535"
/> | <img width="371" height="771" alt="image"
src="https://github.com/user-attachments/assets/92a6fc05-c9ba-4c11-a18c-22b5225d8b9a"
/> |

| Raw mode | All channels |
|---|---|
| <img width="380" height="671" alt="image"
src="https://github.com/user-attachments/assets/c8135600-e3c9-4644-8350-fa5f6b2d3aaa"
/> | <img width="380" height="671" alt="image"
src="https://github.com/user-attachments/assets/bac73a6a-4ed5-42d6-98cc-039a75c48ef3"
/> |

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
## Summary

- Add Devin to the built-in preset harness catalog using the official
native ACP invocation: `devin acp`.
- Link setup guidance to Cognition's official Devin CLI documentation.
- Render a bundled, attributed Devin mark on a white canvas through
Buzz's existing runtime-icon system.
- Keep preset capability metadata in the Rust catalog; no duplicate
TypeScript runtime table or React runtime checks.
- Move the existing preset catalog and its focused tests into a Rust
submodule without changing existing preset behavior, keeping the touched
files within the repository's file-size limit.

### Related issue

Follow-up to the generic BYOH harness work in block#2773.

### Scope

This is the small preset/data-entry follow-up described in the block#2773
discussion. It uses the generic preset readiness contract and does not
add Devin-specific authentication probing, permission bypasses, model
switching, cloud handoff, or cloud Devin capability claims.

The preset supplies:

- ID: `devin`
- Executable: `devin`
- Arguments: `acp`
- Installation guidance: https://docs.devin.ai/cli

### Testing

Local verification was rerun at the final PR head,
`7bb9aa6e862a47a5062b5b8234fdb5ce2aae6c1d`.

- Focused Rust preset tests: 7 passed
- Desktop JavaScript tests: 3,768 passed
- Desktop lint, formatting, file-size, and text guards: passed
- Full Tauri test suite: 1,851 passed, 14 ignored
- Root Rust unit-test groups: passed
- Web production build: passed
- Mobile format, analyze, and test suites: passed
- Full repository `just ci`: passed

The branch also merges cleanly with the current Block `main`. The
upstream fork-triggered CI workflow is awaiting maintainer approval;
DCO, Semgrep OSS, and zizmor are passing.

The bundled SVG was rendered and visually inspected in both its source
dimensions and a 512px preview. The cross-language preset-logo guard
verifies that the Devin mapping exists and the asset is present on disk.

Signed-off-by: Mark Fenner <markfenner57@yahoo.com>
…k#3670)

## What Problem This Solves

`test_usage_metrics_lock_has_single_owner_and_releases_on_drop`
hardcodes the **production** advisory lock key (`0x4255_5A5A_4D45_5452`)
on the shared `TEST_DATABASE_URL`. Postgres advisory locks are
per-database, so any live `buzz-relay` pointed at the same DB holds that
key and the test fails (or races the relay tick). Diagnosis time was
burned during block#3268 verification, including near-misses on live dev
relays. Fixes block#3619.

## Why This Change Was Made

Preferred fix from the issue: run the test on a private scratch DB via
existing `create_scratch_db` / `drop_scratch_db` (same pattern as
replica-routing fixtures). Keep the production lock key so the test
still documents the real constant, without colliding with a running
relay.

## User Impact

- Local `cargo test -p buzz-db -- --ignored` no longer fails when a dev
relay is running against the shared test DB
- Safer: no temptation to `pg_terminate_backend` a live relay to "fix"
the test

## Evidence

- Code review of fixture isolation
- Pattern matches existing `create_scratch_db` usage in this file
- Test remains `#[ignore = "requires Postgres"]` (same as before)

## Related

- Issue: block#3619
- None found among open PRs for this exact fix

Signed-off-by: NanoRisk6 <aidashtherapy@gmail.com>
…block#3368)

Windows installs of Goose and other harnesses failed at exactly five
minutes with an empty error (block#2401). The 300s ceiling was killing
installs that were working, just slowly — the Goose step pulls a ~79MB
release asset, and Windows Defender scans every file npm extracts. When
the ceiling fired it discarded the output it had already read, so the
user got a bare timeout string and no way to tell a hang from a large
download.

## The ceiling

`INSTALL_TIMEOUT` is 900s, and the error names the limit: `install
command exceeded the 15m ceiling and was terminated`. It stays a pure
wall-clock ceiling with no inactivity kill — nothing observable
distinguishes a hung installer from one silently transferring a large
artifact, so silence alone never kills an install. A ceiling kill
remains non-retryable; re-running a command that already burned 15
minutes costs the user more time with no plausible path to success.

The child's exit and both stream drains fold into one resumable settle
governed by a single deadline. Waiting on the drains outside that
deadline would let a descendant that outlived the install shell hold the
output pipes — and the per-runtime install guard behind them — open with
no bound, which is the failure the ceiling exists to prevent. So the
deadline path terminates the process group on the normal-exit branch
too: a leader that exited with a real status still gets its stragglers
killed, and the guard cannot stick either way. Whether the leader had
already exited only decides the verdict — its real status outranks a
timeout.

The install shell is a session leader and its descendants inherit the
output pipes, so signalling only the leader left them running and the
drains blocked on a pipe nobody would close. Escalation keys off the
*group's* liveness rather than the leader's, since a descendant that
ignores SIGTERM outlives the leader and would otherwise never receive
the group SIGKILL. Reaping the killed child and finishing the drains
share one bounded grace, so a termination that failed outright cannot
extend the ceiling that just fired.

## Output capture

Each stream drains into a bounded capture that is *shared* with the
reader rather than returned by it, so whatever arrived before a stall is
readable at the ceiling — exactly when the output matters most. Output
of any size costs a fixed amount of memory.

One capture holds two independently bounded views of the same bytes:

| View | Head / tail | Cut marker |
|------|-------------|------------|
| UI (`InstallStepResult`) | 512 B / 1024 B | `... (N bytes omitted)
...` |
| Log file | 128 KiB / 128 KiB | `... [N bytes omitted at cap] ...` |

The UI budget is screen space; the log's is disk. Both markers are
inline, so neither ever implies completeness it does not have. Both ends
are cut at arbitrary byte offsets, so a partial character is trimmed and
the partial token each cut left behind is dropped — the marker's byte
count includes both trims.

## Install log

`steps` carries only the last attempt of each step, truncated for
display. Everything else — earlier retries, the prerequisite step that
actually broke, the managed-Node bootstrap — used to be discarded.
`InstallReporter` now appends one self-contained record per attempt of
per step to `install-<runtime-id>.log` beside the agent logs, and
`InstallRuntimeResult.log_path` carries the file to the UI, where a
failure message ends with `Full log: <path>`.

Each record is bounded independently by the log-scale capture that
produced it, so a first attempt that printed megabytes cannot push out
the later record explaining the failure; the run's total is bounded by
steps × attempts × per-record cap. Every early return builds its result
through one `InstallReporter::failed` helper, so no failure path can
omit the log pointer, and synthesized steps go through `record_step` — a
step that reaches the UI without passing it would be invisible in the
file.

Install output can echo a registry token or proxy credential from the
environment it ran in, and the file is written unattended. Redaction
keys off the *names* of the environment variables the install inherited,
snapshotted once per run, rather than a list of known secret value
prefixes: a credential with no recognisable shape is exactly the one a
prefix match misses. Three name rules apply, because the variables need
different treatment:

| Rule | Variables | Redacted |
|------|-----------|----------|
| URL userinfo | `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`,
`NPM_CONFIG_PROXY`, `NPM_CONFIG_HTTPS_PROXY`, `NPM_CONFIG_REGISTRY` |
`user:password` only |
| Exact name | `NPM_CONFIG_KEY`, `NPM_CONFIG__AUTH`, `NPM_CONFIG_OTP` |
whole value |
| Marker substring | `*TOKEN*`, `*SECRET*`, `*PASSWORD*`, `*_PAT`, … |
whole value, 8-byte floor |

A proxy or registry keeps its host and port, because an install that
fails behind one is diagnosable only if the record still says which one
it went through, and a bare `user@` with no password is not treated as a
credential. npm's own settings are listed by exact name rather than
matched on `KEY` or `AUTH` substrings — both occur throughout an
ordinary environment on values that are paths and people's names — and
they bypass the 8-byte floor, since a six-digit one-time password is a
credential at that length. Matching is case-insensitive, which is what
npm's lowercase `npm_config_*` spelling needs. `0o600` is set by the
create rather than a later `chmod`, which would leave a window where the
umask decides. A runtime id that cannot safely be a filename yields no
log rather than a sanitized one — a rewritten id could collide with
another runtime's log.

The file holds exactly one run. A run opens its own session after the
runtime id has been canonically resolved — the previous file rotates to
`.1` and any older `.1` is removed before the rename, since a rename
that will not replace its destination would otherwise wedge rotation
permanently on Windows. The session writes a header naming the runtime,
the app version (`app.package_info().version` on the Rust side — cannot
be mocked or fail), the OS (`std::env::consts::OS`), and the start time:
a Windows failure and a macOS one on the same runtime are different
bugs, and a stale app version explains a failure that no longer
reproduces. Each record carries its attempt's elapsed time.

## Live output line

A 15-minute ceiling with nothing behind it but a spinner is
indistinguishable from a hang. The same drain seam feeds an
`acp-install-output` event carrying the newest complete line, and the
three install entry points — Doctor harness rows, the harness catalog
dialog, and onboarding runtime cards — render it under the spinner with
`aria-live="polite"`.

Ordering is keyed on a `seq` monotonic across the whole install, not on
the attempt number, which restarts at 1 for every step: keyed on
attempt, one step succeeding on attempt 2 would make the next step's
attempt-1 output look stale and freeze the display for the rest of the
install. Each executed attempt begins with an unthrottled `line: null`
clear signal, so a stale failure line cannot sit under the spinner while
the retry runs. Events are otherwise throttled to four per second, and
the throttle *retains* the newest pending line and flushes it when the
window reopens rather than dropping it — at an attempt boundary a drop
would silently eat the new attempt's first line.

The subscription is mounted for the runtime's whole lifetime rather than
started when the install begins. The install command is invoked from the
click handler, so the clear and a fast command's first lines can be
emitted before React has committed the pending state, and nothing
replays them — a subscription that waited for that state would lose the
entire output of a short install. The run boundary resets the ordering
key when the install settles, since `seq` restarts for the next run, and
the line renders only while installing, so a straggler from a finishing
drain cannot appear under a fresh Install button.

The 15-minute ceiling deliberately stops waiting on stuck drain threads
— a hung installer must not freeze the app. That means a drain thread
can outlive its `InstallReporter`. Without a generation guard, a drain
that calls `offer` after the run settles would publish an event with the
run's high `seq`, poison the permanent listener's React state, and cause
the next install's restarted `seq=0` events to be rejected. `Live` now
carries a `lifecycle: Arc<RwLock<bool>>`; drain threads hold a **shared
read guard** from the admission check through the `(self.emit)(...)`
call, making the check-then-emit pair atomic with respect to shutdown.
`InstallReporter::drop` takes the **exclusive write guard** and stores
`false` — this blocks until every in-flight drain publication releases
its read guard, then prevents any new admission. Deactivation is
bounded: the write lock holds only for the flag store, so it can block
at most for the duration of one emit call (microseconds to low
milliseconds). Rust drops locals in reverse-declaration order, so
`reporter` drops before `_guard`, ensuring the exclusive write completes
before the per-runtime concurrency guard releases and a new install can
start.

## Also

Install result types move to `desktop/src/shared/api/installTypes.ts`,
following the existing `searchTypes.ts` / `workflowTypes.ts` convention,
and are re-exported from `tauri.ts` and `types.ts` — both already over
the file-size cap, so neither can grow to carry them.

Two comments described `AdapterOutdated` as applying only to the
deprecated package; it also covers a version below the supported floor.

Report: block#2401

---------

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

- target the visible thread branch collapse guide in the messaging smoke
test
- avoid clicking the underlying collapse rail when the guide overlaps it
- retain the existing post-click assertions that verify the two-reply
branch collapses

## Context

`main` CI failed because Playwright repeatedly attempted to click the
lower `thread-collapse-rail` while the matching `thread-collapse-guide`
intercepted pointer events. Both controls dispatch collapse for the same
branch; the guide is the actual topmost user target and is already used
by `thread-unread.spec.ts`.

Failing run: https://github.com/block/buzz/actions/runs/30575425126

## Validation

- focused Playwright smoke test: 1 passed
- pre-push hooks: desktop check passed; 3,835 desktop tests passed
- `git diff --check`

## Review

Princess Donut reviewed the test-only approach and locator determinism
with no blockers. Mongo review is pending.

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
…block#3358)

Team catalog projections (`kind:30178`) embed every member's system
prompt, so they need the same read gate personas already have: only the
author sees an unshared event. The gate was hardcoded to `kind:30175` at
six read surfaces plus the SQL pushdown, so rather than adding a second
special case it becomes kind-generic over `SHARED_GATED_KINDS = {30175,
30178}`.

## Kind 30178

New parameterized-replaceable kind, addressed by `(pubkey_o, 30178,
team_id)`. It embeds sanitized member projections instead of referencing
`kind:30175` heads — a foreign reader of a shared team could not
otherwise hydrate members whose own persona events are unshared or, for
built-ins, absent entirely. `kind:30176`'s wire body is untouched, so
device sync keeps its contract.

## Kind-generic shared gate

`buzz_core::kind` replaces `is_persona_shared_kind` /
`is_unshared_persona_event` / `persona_event_is_shared` with
`SHARED_GATED_KINDS` and the kind-agnostic `is_shared_gated_kind` /
`is_unshared_gated_event` / `event_is_shared`. Every read surface
consults the set:

| Surface | File |
|---|---|
| REQ historical delivery + `ids` lookup |
`crates/buzz-relay/src/handlers/req.rs` |
| Live fan-out | `crates/buzz-relay/src/handlers/event.rs` |
| COUNT fallback | `crates/buzz-relay/src/handlers/count.rs` |
| NIP-98 HTTP `/query`, `/count`, `/search` |
`crates/buzz-relay/src/api/bridge.rs` |
| Pre-`LIMIT` SQL pushdown | `crates/buzz-db/src/event.rs` |

The SQL clause generalizes from `kind != 30175` to `kind NOT IN (...)`
bound from `SHARED_GATED_KINDS`, still applied before `ORDER BY … LIMIT`
so a page of newer private events cannot starve an older shared one off
the candidate set. `EventQuery::persona_reader` is renamed
`shared_gated_reader` and `needs_persona_filtering` to
`needs_shared_gate_filtering` to match.

Because the `buzz-core` rename has consumers outside the relay, the four
desktop call sites of `persona_event_is_shared` travel with it:
`desktop/src-tauri/src/commands/personas/pending.rs`,
`desktop/src-tauri/src/event_sync.rs`, and two in
`desktop/src-tauri/src/managed_agents/persona_events.rs`. Each call is
unchanged apart from the name — the persona `shared` projection behaves
exactly as before.

## Ingest validation

`validate_persona_envelope` splits into two reusable pieces —
`validate_shared_tag` (exactly-two-element `["shared","true"]`, at most
one occurrence) and `single_bounded_d_tag` (exactly one `d` tag,
non-empty, `<=64` chars, no ASCII control characters or whitespace).
`validate_team_catalog_envelope` composes both; personas additionally
keep the slug grammar `^[a-z0-9][a-z0-9_-]{0,63}$`.

`kind:30178` deliberately does **not** get the slug grammar. Team ids
are UUIDs or built-in identifiers such as `builtin-team:welcome`, and
the colon is not slug-legal; rewriting ids to fit would break NIP-33
addressing against the team's own `kind:30176` head. The non-empty and
exactly-one checks are load-bearing regardless — without them generic
NIP-33 storage maps a missing `d` onto `(pubkey_o, 30178, "")` and every
team overwrites its predecessor.

The exact two-element `shared` shape is enforced because the SQL
visibility clause is JSONB containment (`tags @>
'[["shared","true"]]'`), which would match a three-element superset such
as `["shared","true","extra"]`.

`kind:30178` is also added to the `Scope::UsersWrite` allowlist and to
`is_global_only_kind`, so a stray `h` tag cannot channel-scope an
owner-authored definition.

## Deferred

`kind:30176` is deliberately not a gate member. Its writers never emit
`shared`, so catalog opt-in semantics do not describe it — it needs
owner-private reads driven by an authenticated principal set, tracked as
a separate follow-up.

## Tests

- 19 new `ingest.rs` unit tests covering the 30178 envelope (UUID and
colon `d` tags, 64-char boundary, non-ASCII bound,
empty/valueless/duplicate/missing `d`, embedded newline, `shared`
false/three-element/duplicate, scope and global-only membership).
- Persona regressions for the valueless `["d"]` shapes, since the
`d`-tag helper is shared by both validators.
- Existing `kind.rs` gate tests generalized and extended to assert the
gate applies to 30178 as it does to 30175.
- New `crates/buzz-test-client/tests/e2e_team_catalog.rs`: 9 WS-level
tests over a live relay covering author reads of unshared heads, foreign
omission from REQ, `ids`-lookup denial, COUNT existence-leak, share and
unshare transitions, and the mixed-kind filter case.
- `.github/workflows/ci.yml` adds `--test e2e_team_catalog` to the Relay
E2E job so the new suite runs.

## Docs

`docs/nips/NIP-AP.md` gains a "Team catalog projection: kind:30178"
section and an "Ingest validation: kind:30178" subsection, records the
gate as kind-generic, documents 30178 deletion vs. unshare semantics,
and adds a security note that sharing a team exposes every member's
instructions even when that member's own `kind:30175` head is unshared.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
devin-ai-integration Bot and others added 29 commits August 1, 2026 05:27
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Co-authored-by: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Integrate project thread workflow status on top of upstream desktop-v0.5.3.
Keep upstream presets.rs bundled-logo architecture and drop the superseded
remote-avatar preset_harnesses path from the Crew branch.

Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Combine retryable CLOSED resubscribe with resume flush and lifecycle
health tracking in the mobile relay session.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Use one messages list for both resume-health and CLOSED-retry tests.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
Keep ChannelPane under the 1000-line file-size ratchet after merging
agent activity visibility into the v0.0.6 cut.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: oscarlehuu <oscarlehuu@users.noreply.github.com>
@cursor
cursor Bot merged commit ac8eef0 into main Aug 1, 2026
6 checks passed
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.