Commit Graph

2170 Commits

Author SHA1 Message Date
Wes
21a4f03b8b desktop: satisfy relay authority CI gates
Format the Tauri changes, use transaction auto-deref accepted by Windows Clippy, and split oversized desktop modules along existing responsibility boundaries so the file-size ratchet remains enforceable.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 14:47:21 -06:00
Wes
25fca37e01 desktop: retire conflicted aggregate attempts
Delete an exact pending edit attempt after a proven relay conflict so the next edit can advance from newly hydrated authority instead of being blocked by a dead retained generation.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:40:56 -06:00
Wes
d88f3f1544 desktop: close relay-authority retry gaps
Retire proven conflicts while preserving transport retries, seed reconstructed relay heads into retention, preserve device-local policy during inbound aggregate advances, and suppress fenced legacy persona publication.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:40:12 -06:00
Wes
e7fca223c3 desktop: document migration builder argument seam
The migration builder deliberately accepts the complete cryptographic candidate inputs; suppress the argument-count lint at that boundary rather than obscuring them behind an unrelated bag type.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
3b7af206fd desktop: route authoritative agent edits through relay CAS
Build generation-advancing aggregates from the retained definition binding, submit and verify before advancing local authority evidence, suppress fenced legacy projection writes, and roll back local edits when relay confirmation fails.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
f960d5665e Revoke deleted managed agent participation
Add an owner- and community-bound durable tombstone predicate and enforce it
across shared transport admission, ingest, and live WebSocket commands. Await
cluster-wide disconnect publication after tombstone commits so exact retries
repair failed propagation.

Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
33c41d10c3 desktop: make relay-canonical deletion crash safe
Retain authoritative tombstones before local destruction, preserve terminal deletion evidence, submit and verify deleted aggregates, and apply local erase with a marker-before-erase replay protocol. Keep legacy deletion immediate and make persona cascades resumable across partial enqueue/save failures.

Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
ddf10f96a4 desktop: test private agent reconstruction directly
Build and decrypt a real private managed-agent aggregate in the inbound reconciliation test module, then assert portable secrets, backend configuration, relay mesh, and authoritative head evidence survive reconstruction.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
94b96752c9 desktop: hydrate managed agents before Welcome provisioning
Make the owner-scoped persona sync backfill awaitable and run it before Welcome onboarding inspects local managed agents. This lets a second installation reconstruct relay-canonical identities before deciding whether to mint replacements.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
57bb681b69 relay: fence private agent heads from NIP-09 deletion
Reject both a-tag and e-tag kind:5 attempts to soft-delete kind:30179 at the handler and database boundaries. Extend the managed-agent integration assertion to cover the private head alongside its bound projections.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
94b0a6e83a desktop: reconstruct relay-canonical agents
Subscribe to owner-private PMA heads, strictly decrypt and validate their bound projections, and reconstruct the portable managed-agent record on a fresh device without allowing unsolicited heads to overwrite LegacyOnly state. Carry the relay-mesh marker that the field classification already declares private-canonical.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
1d702afc21 desktop: activate PMA migration on capable relays
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
27f3143e61 desktop: preserve PMA retry through promotion
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
fcdaf22232 desktop: PMA aggregate submit/retry driver
Add `managed_agents::migration::driver`, the transport half of the
relay-canonical migration seam. It is the only code that moves a retained
aggregate across the wire: it consumes one immutable row from
`managed_agent_aggregates`, POSTs the row's `request_json` verbatim to
`/api/managed-agents/aggregate` with fresh owner-signed NIP-98 auth,
strict-deserializes the corrected `migration::AggregateResponse`, verifies it
through `verify_promotion`, and flips the durable row to synced only after a
fully verified read-back.

Design invariants:
- Exact bytes on the wire. The body is `request_json` verbatim; the driver
  never re-serializes it, so the stored request and the request the relay
  authenticates are the same bytes. The verification source
  (`MigrationCandidate`) is reconstructed from those same bytes — parse the
  three signed events, decrypt the head under the owner key — so the candidate
  and the wire request cannot diverge and no build-side crypto is duplicated.
- Fresh owner NIP-98 per attempt via `build_nip98_auth_header_for_keys` (unique
  nonce), so a retry never replays a stale token. Mirrors the `/events`
  submit path: egress guard, rate-limit wait, `classify_request_error` /
  `relay_error_message` / `parse_json_response`.
- Only the exact retained (generation, private_event_id) is marked synced.
  Beyond `verify_promotion`'s response-vs-candidate checks, the driver binds
  the *durable row's* coordinate to the verified evidence, so a row whose
  columns disagree with its own request_json (and thus the verified head) is
  never confirmed.
- Errors preserve retry. Every failure path — transport, non-2xx, malformed
  body, reconstruction, verification, coordinate mismatch — persists an
  exact-attempt diagnostic via `record_managed_agent_aggregate_error` and leaves
  `pending_sync = 1`. Nothing here clears retry except a verified read-back.

Transport tests use a loopback axum stub in the shape of the aggregate route
(6, all green): faithful read-back marks the exact generation synced and posts
verbatim; conflict status preserves retry; malformed success body preserves
retry; tampered read-back fails verification and preserves retry; row
coordinate disagreeing with the verified head is not synced; no pending row is
a no-op with no network submission.

Pure transport confined to the new module + one `mod` line; no delete/tombstone,
docs, or relay-crate changes. `managed_agents::` suite: 1026 passed, 0 failed;
`cargo check --lib` clean.

Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:54 -06:00
Wes
853dee620c desktop: enumerate pending PMA attempts
Snapshot the latest pending retained aggregate per agent within the captured owner scope so the flush driver can drain immutable retry inputs without crossing identities or replaying superseded generations.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
82dc81625e desktop: verify PMA deletion read-back
Require deleted aggregate responses to return the exact retained tombstone, echo its generation and event ID, and omit all active projections before an attempt may be acknowledged.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
7908cd066e desktop: route authoritative deletion through PMA
Preserve authority evidence through single-agent and persona-cascade deletion, enqueue a minimal next-generation aggregate tombstone for relay-authoritative agents, and retain legacy kind:5 deletion only for LegacyOnly records.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
aa7efe0e32 desktop: build private agent tombstones
Add a pure owner-signed deleted aggregate builder that advances verified relay authority evidence without carrying active projections, with codec round-trip and invalid-input coverage.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
4da22a7635 Document PMA fence and migration lock cost
Define the last-binding behavior for shared definition coordinates and call out migration 0028's ACCESS EXCLUSIVE full-table rewrite before capability rollout.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
467e67bee5 Fence relay-managed agent authority races
Serialize aggregate instance projections with ordinary NIP-33 writes, recheck PMA authority inside the replacement transaction, and prevent both NIP-09 deletion paths from mutating bound projections. Add real-Postgres coverage for the first-aggregate ingest race and kind:5 a/e-tag fencing.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
b4abf61255 desktop: bind verifier to aggregate response contract
Deserialize the exact aggregate route shape, require its acceptance, head ID,
generation, state, optional active projections, and definition revision to
match the signed payload, and compare the complete served projection events
to their recovery copies.

Add route-shape and response-metadata regression coverage.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
394b4c1600 desktop: pure relay-canonical managed-agent migration builder + verifier
Add `managed_agents::migration`, the first real consumer of the slice-1/2
authority contract. It converts one hydrated `ManagedAgentRecord` + owner/agent
keys + exact signed kind:30175/30177 projections + CAS coordinate into the
shared `buzz_core::private_managed_agent::Payload` and a signed, inert
kind:30179 aggregate candidate, and verifies the relay read-back byte-exact
before yielding promotion evidence.

Pure by construction: no HTTP, no persistence, no authority mutation. The
transport/driver lane (sibling) owns submission, retry, durable state, and the
actual RelayAuthoritative write. This module is the gate those lanes pass
through, so every "is the relay head a faithful owner-signed copy?" check lives
here behind an adversarial fixture matrix.

Builder (`build_migration_candidate`):
- `PrivateConfig.backend` carries the versioned `VersionedBackend` envelope, not
  a bare `BackendKind`, so a shape change is an explicit version bump.
- `auth_tag` is RE-MINTED unconditionally via `compute_auth_tag(owner, agent,
  "")`; the stored record string is never copied or trusted (a re-mint cannot
  byte-match it, and the codec requires an unconditional attestation).
- Readiness gating derives the ACTUAL largest inserted codec `Value` (the two
  projection recovery envelopes dominate) plus the encrypted payload size and
  feeds them to `assess_migration_readiness` with the relay's NIP-11 tokens —
  never a caller-supplied number.
- Fails fast on inconsistent keys/projections; `build_event` re-runs full codec
  validation so any semantic slip surfaces locally, not at the relay.

Verifier (`verify_promotion`): treats the aggregate response as a LOCAL typed
fixture matching the documented route JSON, not a buzz-db type. Checks, in
order: `validate_and_decrypt` of the returned head; owner/agent/generation/
predecessor/state equality; binding event-id + content-hash + recovery equality
against the returned signed projections (and that the served standalone
projection matches its binding, so a swapped public event is caught); definition
revision equality; and source-vs-roundtrip equality for CARRIED classes only
(the payload type structurally carries only PrivateConfig + identity, i.e. the
carried classes — derived/transient/bookkeeping fields never enter it). Returns
`PromotionEvidence` only after every check passes.

Adversarial fixtures (14, all green): happy exact round-trip; re-mint-not-copy;
versioned backend envelope; agent-key/record-pubkey mismatch; projection not
owner-signed; wrong-kind projection; capability-absent block; oversize codec
value block; tampered head ciphertext; head signed by wrong owner; swapped
definition projection; swapped instance projection; wrong definition revision;
stale-generation head. Uses shared codec kind/size constants directly.

Foundation API is `#[allow(dead_code)]` with a named sibling consumer (the
transport lane); prod `cargo check --lib` is warning-free. `managed_agents::`
suite: 1014 passed, 0 failed.

Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
f0501d6c93 test(search): load renamed private agent migration
Point the FTS integration fixture at the folded migration filename so the
private kind:30179 exclusion coverage compiles and can run.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
20eea67663 desktop: retain private agent aggregate attempts
Persist each exact signed kind:30179 aggregate request by generation so
retries reuse immutable bytes and newer tombstones do not erase older
in-flight attempts. Enforce contiguous generations and exact per-attempt
compare-and-clear/error updates.

Add focused coverage for immutable generations, stale acknowledgements, and
diagnostic scoping.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
6644d48796 desktop: fence legacy reconcile after relay promotion
Skip kind:30177 boot reconciliation for records whose verified kind:30179
head is canonical. This prevents stale local compatibility JSON from
republishing a legacy projection after promotion or deletion.

Add a regression proving an authoritative record creates no retained or
pending kind:30177 row.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
2f987007a9 desktop: migration-readiness contract for relay-canonical authority
Add the migration-readiness contract on top of the authority foundation
(FIX #2, FIX #5). Types + pure gate only — no migration enablement, no
destructive local mutation. The migration driver (sibling lane) is the
consumer.

- Reclassify `auth_tag` PrivateCanonical -> DerivedNotCarried: it is
  re-minted at spawn from owner keys + agent pubkey (compute_auth_tag),
  never carried in a projection and never diffed at verification. Carrying a
  stale tag would pin an agent to legacy forever.
- Add a `VersionedBackend` envelope with `BACKEND_PAYLOAD_VERSION = 1` and a
  stable serialized shape; deny-unknown-fields so a newer envelope is
  rejected rather than silently truncated. Round-trip + reject-unknown
  fixtures.
- Add the `MigrationBlock` failure taxonomy and the pure
  `assess_migration_readiness` gate (FIX #5): capability FIRST, then payload
  size, then per-value size. Capability is the exact NIP-11 aggregate token
  `nip-pma-aggregate-v1` (NIP_PMA_AGGREGATE_TOKEN) matched over the advertised
  token set — not a boolean "kind accepted". Size boundaries are
  inclusive-safe; exactly-at-limit passes, strictly-over blocks. On any block
  the caller MUST stay LegacyOnly and NOT retry until an input changes (no
  timer/retry loop).
- `MIGRATION_MAX_VALUE_BYTES = 32_768` tracks the relay per-`Value` codec
  limit (`private_managed_agent::MAX_VALUE_BYTES`); it is NOT a keyring floor
  or a combined recovery blob. `MigrationBlock::reason()` renders a diagnostic
  string for logging/surfacing — the variant is the signal; durable
  persistence is owned by the migration driver record, not this gate.

These are foundation contract types; the next slice wires them into a real
payload builder / verification consumer so they prevent drift rather than
sit beside `PrivateConfig.backend: Value` decoratively.

Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
a9f93ff420 Cover private managed-agent tombstone authority
Extend the migrated-Postgres aggregate regression through deletion. Prove a
private tombstone advances the CAS generation, returns no public bindings,
retires the instance projection, retains the ordinary-ingest authority fence,
and rejects a same-generation stale active resurrection.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
e972169768 desktop: relay-canonical managed-agent authority foundation
Introduce the Desktop safety foundation for the relay-canonical managed-agent
migration. State + classification only — no migration enablement, no
destructive local mutation. Every existing record defaults to LegacyOnly, so an
upgrade that predates any migration deserializes unchanged and keeps local JSON
+ keyring authoritative.

- Add `managed_agents::authority` with a single internally-tagged
  `RelayAuthority` enum (`LegacyOnly` | `RelayAuthoritative { evidence }`), so
  impossible states are unrepresentable: authoritative structurally carries a
  verified head, legacy structurally cannot. No `{"state":{"state":...}}`
  nesting — persisted shape is `{"authority":"legacy_only"}` /
  `{"authority":"relay_authoritative","evidence":{...}}`, asserted exactly.
- Add `#[serde(default)] relay_authority: RelayAuthority` to
  `ManagedAgentRecord`; a store predating the field deserializes as LegacyOnly.
- Add an exhaustive field→codec-home classification table
  (`FIELD_CLASSIFICATIONS`) as a single source of truth, verified against the
  ACTUAL PersonaEventContent (30175) / ManagedAgentEventContent (30177)
  builders and the 30177 exclusion asserts — not intent:
  * start_on_app_launch, auto_restart_on_config_change, provider_binary_path,
    persona_team_dir carry through NO codec slot -> TransientLocal.
  * avatar_url IS carried by 30175 -> stays DefinitionProjected.
- Tighten the exhaustiveness test to exact set equality: every record field has
  a class AND every class key still names a live field (no stale drift).
- Populate `relay_authority` explicitly (`RelayAuthority::legacy()`) at every
  production/test constructor rather than relying on a struct-wide default.

Foundation-only classification/authority API is consumed by the not-yet-landed
reconcile/migration lanes; suppression is per-item with a named consumer, not
module-wide.

Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
3451593743 Add relay authority for private managed agents
Introduce an authenticated aggregate endpoint and serializable authority
transaction for encrypted private managed-agent heads and their exact public
projections. Preserve immutable signed recovery bytes, enforce generation and
definition revision CAS, reactivate submitted projections, retire prior heads,
and return a writer-consistent signed aggregate snapshot.

Keep kind 30179 author-only and out of full-text search, reject generic writes,
and advertise the aggregate capability through NIP-11.

Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:53 -06:00
Wes
4018a9ab57 Cover private aggregate filter classification
Lock explicit, mixed-kind, and kindless private managed-agent filters to the
author-only SQL pushdown path.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:52 -06:00
Wes
87c5e5d2b8 Clarify private managed-agent rollout contract
Document the encrypted relay boundary, Desktop verification gate, shared
definition revision semantics, runtime capability gate, and bounded deployment
phases for the relay-authority follow-up.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:52 -06:00
Wes
2efbdd52c4 Cover author-only SQL privacy regressions
Add Postgres-backed coverage for pre-limit owner visibility, kindless ID
privacy, and explicit, mixed, and kindless COUNT behavior.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:52 -06:00
Wes
ff05a1ce0c Enforce author-only event visibility in SQL
Push the authenticated reader into event query and count predicates before
pagination, including mixed-kind and kindless filters. Add the brownfield FTS
exclusion for private managed-agent aggregates without changing prior migration
checksums.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-05 13:21:52 -06:00
klopez4212
f2ce575b62 Fix media attachment actions (#4849)
## Summary

- Upload photos immediately while keeping videos queued for background
upload.
- Move image annotation and video spoiler actions to thumbnail hover
overlays.
- Preserve the image editor's existing Draw and Spoiler controls.

### Snapshots

#### Image annotation overlay

![Image annotation
overlay](https://raw.githubusercontent.com/block/buzz/87d7e1b1a0774ffef7a1a6cba03baffd63e11bd4/pr-4849--01-image-annotation-overlay.png)

#### Image editor controls

![Image editor
controls](https://raw.githubusercontent.com/block/buzz/87d7e1b1a0774ffef7a1a6cba03baffd63e11bd4/pr-4849--02-image-editor-controls.png)

## Testing

- `pnpm typecheck`
- `pnpm check`
- Focused attachment, drawing, and spoiler smoke tests
- Pre-push desktop tests (4,286 passing)

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Honey <47c18026466e670a1618fd7de0ef32b9ff75d6e0b5ccf255d13c8c3d674ed115@buzz.block.builderlab.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-08-05 12:17:48 -07:00
klopez4212
2034e693a8 fix(desktop): remove join API token control (#4897)
## Summary

Remove the nonfunctional API-token option from the existing-community
join flow.

## Validation

- Focused Playwright join-flow coverage
- Add-community screenshot coverage

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-08-05 11:56:14 -07:00
Wes
014562c063 fix(desktop): allow shared agent mentions (#4913)
## Summary

- admit relay-discovered agents to autocomplete when their response
policy authorizes the viewer
- require authorization in the exact active stream/forum channel for
mentions, while keeping community-wide discovery for member invitation
- fail closed for relay-only agents in DMs and unresolved composer
contexts
- re-authorize cached autocomplete rows after policy/channel changes so
stale agent suggestions cannot leak back in
- preserve managed-agent behavior and explicitly reject stale
agent-marked channel members absent from both live directories

## Validation

- `pnpm --dir desktop test` — 4,288 passed
- `pnpm --dir desktop typecheck`
- `pnpm --dir desktop check`
- `pnpm --dir desktop build:e2e`
- focused Playwright mention matrix — 12 passed
- focused Playwright member-invitation matrix — 2 passed
- pre-push hooks after rebase to current `origin/main` — desktop check
and 4,288 tests passed
- independent correctness/privacy re-review cleared with no remaining
blocker

## Related competing PRs

This supersedes or overlaps #2333, #3056, #4242, #4137, #2314, #4058,
and #2605. This version adds exact-channel authorization, fail-closed
DM/context handling, cached-row reauthorization, forum coverage,
outbound mention-tag coverage, explicit stale-member coverage, and
add-member discovery coverage.

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-08-05 11:15:11 -07:00
klopez4212
ff0b7982f1 Polish mobile top navigation (#4778)
## Summary

- Polish mobile Home, Activity, Search, and Settings navigation chrome.
- Add progressive Buzz gradients/frost, aligned theme colors, dividers,
typography, and section spacing.
- Refine Search and Settings motion, including automatic keyboard focus
on search activation.

<img width="630" height="1368"
alt="C78FA3CE-F2B3-45F2-B9F5-7EA7500778CC"
src="https://github.com/user-attachments/assets/5935514b-d894-4010-80dd-a938363fee93"
/>
<img width="630" height="1368"
alt="5C058A73-1879-476A-881C-531ACC256D84"
src="https://github.com/user-attachments/assets/5266c841-17ee-49e8-9841-b06d84f4195f"
/>
<img width="630" height="1368"
alt="3F50ADB7-9BDA-4A8D-A81E-20560C3B9EA6"
src="https://github.com/user-attachments/assets/f615f61e-9e96-4bce-b261-ae5ec54db872"
/>
<img width="630" height="1368"
alt="35ECE741-01F3-4B79-80C5-1DDD447121A7"
src="https://github.com/user-attachments/assets/b5145186-9884-44eb-8ebc-f3303831c0a4"
/>

## Validation


- `flutter analyze`
- Focused Home, Activity, Channels, Search, theme, and footer widget
tests
- Full pre-push checks, including mobile tests, desktop checks, and
Tauri checks
- On-device iPhone review during the visual polish pass

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: npub1glqcqfjxdens59scl477pmejh8lht4hqkhx0y4w38jxr6e6w6y2sm29y4e <47c18026466e670a1618fd7de0ef32b9ff75d6e0b5ccf255d13c8c3d674ed115@buzz.block.builderlab.xyz>
Signed-off-by: Code Reviewer <037593536284cf40e221c96c931e9877d4166d54f6bb84e5341a86d7fd5d05a4@buzz.block.builderlab.xyz>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Co-authored-by: npub1glqcqfjxdens59scl477pmejh8lht4hqkhx0y4w38jxr6e6w6y2sm29y4e <47c18026466e670a1618fd7de0ef32b9ff75d6e0b5ccf255d13c8c3d674ed115@buzz.block.builderlab.xyz>
Co-authored-by: Code Reviewer <037593536284cf40e221c96c931e9877d4166d54f6bb84e5341a86d7fd5d05a4@buzz.block.builderlab.xyz>
2026-08-05 19:01:20 +01:00
Wes
4674750b7e fix(release): tag immutable desktop candidates (#4811)
## Summary

Redesign the permanent Desktop release flow so unrelated merges to
`main` cannot invalidate an already reviewed, green release candidate.

- Tag the immutable, API-confirmed release PR head instead of its later
squash commit.
- Treat the merged PR—including an authorized owner/admin bypass—as
publication authorization, while requiring trusted check evidence that
was complete at merge time.
- Make tag creation idempotent and collision-safe: an existing tag
succeeds only at the exact candidate SHA, and create races refetch
before accepting equality.
- Replace ancestry-based previous-release discovery with a validated
metadata ledger for side-history candidate tags.
- Compute the next release from the prior frozen base to the new frozen
base, excluding only the prior release squash SHA so unrelated commits
remain in the changelog.
- Preserve schema-1 production-tag migration and reject malformed
metadata or equal/decreasing versions.
- Update operator documentation for the normal squash-merge workflow.

This is the reusable release process for `0.5.6` onward, not the retired
one-shot `0.5.5` recovery path.

### Invariants covered

- Candidate creation → unrelated `main` merge → authorized squash merge
→ immutable candidate tag.
- Trusted producer IDs and merge-time completion timestamps; DCO's
bounded post-merge exception remains isolated.
- Missing/spoofed checks, tampered candidates, ambiguous PR
associations, conflicting tags, and equal/decreasing versions fail
closed.
- Same-SHA retries succeed; different-SHA collisions fail.
- Legacy schema-1 tag-on-main migration and schema-2 side-history
accounting both preserve the correct next-release changelog.

### Related issue

N/A — follows the Desktop release failures in #4788 and #4800 and the
recovery revert in #4808.

### Testing

At clean commit `6a91fbed8147a48cf174997de0c3e4cb2fb26474`:

- `scripts/test-desktop-release-candidate.sh`
- `scripts/test-release-ref-contract.sh`

Both focused suites passed with HEAD unchanged. Princess Donut cleared
the security/provenance surface, including the hostile merge-time
timestamp cases. Mongo cleared the side-history ledger, migration,
version-order, documentation, and contract-test surface.

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-08-05 11:00:14 -07:00
Jordan Mecom
efe1893dd3 fix(channels): restrict private-channel invitations (#4612)
This change requires an active owner or administrator for third-party
additions to private channels. The relay validator and transactional
database authority enforce the same rule, including removed-member
reactivation and role-change paths.

Idempotent self-target behavior remains available, while ordinary
members can no longer extend private-channel access to another identity.

## Testing

- `git diff --check
origin/main...codex/security-private-channel-invite-authority`
- Rebased onto `origin/main` at `5c98932`
- Full CI pending

Originating Buzz thread:
`buzz://message?channel=3928fe05-df61-4b5d-b9c7-d623b9b10ea1&id=3c6c02312f763fbe0d2bfc33a6c1a362f91d0354f3d18b039cf7a0558c1439d1`

---------

Signed-off-by: Jordan Mecom <jm@squareup.com>
Signed-off-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Eli Foster <efoster@squareup.com>
2026-08-05 10:47:00 -07:00
Jordan Mecom
ad538bfb1e fix(acp): reject unattended permission requests (#4609)
This change removes the ACP permission-bypass mode, defaults managed
sessions to `dontAsk`, and answers permission requests with
`reject_once` or cancellation in both ACP read loops.

Unattended operations that require interactive approval now fail closed
instead of being silently authorized. Explicit non-interactive modes
that do not bypass a permission request remain available.

Both layers have to change together: `apply_permission_mode` treats an
unsupported mode and a failed `set_config_option` as non-fatal by
design, so a request can still reach the harness even in a
non-interactive mode. Removing `bypassPermissions` from the enum rather
than only changing the default means the mode cannot be restored by
configuration alone.

The scope of the guarantee is that `buzz-acp` never grants approval. An
agent that pre-authorizes tools in its own configuration (for example
Claude Code's `settings.json`) still runs them without asking, which is
outside this harness.

## Testing

- `env -u BUZZ_ACP_LAZY_POOL bin/cargo test -p buzz-acp` at `16fff4d`:
671 library tests and 9 integration tests passed
- `cargo clippy -p buzz-acp --all-targets -- -D warnings` and `cargo fmt
-p buzz-acp -- --check`: clean
- `git diff --check
origin/main...codex/security-acp-shell-auto-approval`

The permission tests previously re-implemented the `reject_once` lookup
in the test body instead of calling the code under test, so they would
have passed unchanged if the harness went back to selecting
`allow_once`. They could not call it directly, because
`handle_permission_request` is a method on `AcpClient`, which owns a
live `Child` and its stdio pipes. The choice is now a free function,
`permission_denial_response`, and the tests exercise it: `reject_once`
preferred over offered allow options, the cancelled fallback when no
`reject_once` exists, an empty option list, and a `reject_once` missing
its `optionId`. The cancelled fallback had no coverage before despite
being the fail-closed backstop.

## Operator notes

- `BUZZ_ACP_PERMISSION_MODE=bypassPermissions` no longer parses, so a
process configured with it fails to start rather than silently
downgrading.
- Desktop managed agents do not set a permission mode, so they inherit
`dontAsk`. The desktop has no permission prompt, so operations needing
approval now fail with no in-app way to approve them.

Originating Buzz thread:
`buzz://message?channel=3928fe05-df61-4b5d-b9c7-d623b9b10ea1&id=3c6c02312f763fbe0d2bfc33a6c1a362f91d0354f3d18b039cf7a0558c1439d1`

---------

Signed-off-by: Jordan Mecom <jm@squareup.com>
Signed-off-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 10:38:51 -07:00
Jordan Mecom
885bed35ee fix(workflow): bind trigger author to the signed event (#4607)
This change derives `trigger_author` exclusively from the signed event
pubkey. Actor tags remain available as event data but cannot override
the identity used by author-sensitive workflow conditions.

This removes the impersonation path without changing workflow
definitions or requiring stored-data migration.

## Testing

- `bin/cargo test -p buzz-workflow` at `78819df`: 154 passed, 2
Postgres-dependent tests ignored
- `git diff --check
origin/main...codex/security-workflow-trigger-author`

Originating Buzz thread:
`buzz://message?channel=3928fe05-df61-4b5d-b9c7-d623b9b10ea1&id=3c6c02312f763fbe0d2bfc33a6c1a362f91d0354f3d18b039cf7a0558c1439d1`

Signed-off-by: Jordan Mecom <jm@squareup.com>
2026-08-05 10:35:40 -07:00
Jordan Mecom
997b8caaa4 fix(git): revoke access for banned relay members (#4608)
This change rechecks the durable community ban in the shared Git HTTP
authentication path for advertise, fetch, and push requests. A banned
member is denied even if repository-channel membership still exists, and
restriction lookup errors fail closed.

The additional database lookup happens on every Git HTTP request so
access revocation does not depend on stale session state.

The check also cascades to the NIP-OA owner. Git accepts NIP-OA
attestations on the NIP-98 token, so an agent key can act for its owner
— without the cascade, a banned human would keep clone and push access
through any agent key. This mirrors the NIP-42 gate in `handlers::auth`:
either principal's ban denies the request.

The check runs inside the `GitAuth` extractor, so all three Git routes
inherit it.

## Testing

- `git diff --check origin/main...codex/security-ban-revokes-git`
- Rebased onto `origin/main` at `5c98932`
- `cargo test -p buzz-relay --lib sec005_read_gate_tests`: 8 passed, 7
ignored (Postgres)
- `cargo clippy -p buzz-relay --all-targets -- -D warnings` and `cargo
fmt --check`: clean

Pure tests cover the decision table (agent ban, inherited owner ban, no
attestation). Postgres-gated tests cover the wiring: the real ban row, a
live `compute_auth_tag` attestation, and the 503 fail-closed path.

**Not yet verified:** the three Postgres-gated tests compile and skip
but have not been run — no local Postgres, and CI does not run
`--ignored`. They need `cargo test -p buzz-relay --lib
sec005_read_gate_tests -- --ignored` against a migrated dev database.

Originating Buzz thread:
`buzz://message?channel=3928fe05-df61-4b5d-b9c7-d623b9b10ea1&id=3c6c02312f763fbe0d2bfc33a6c1a362f91d0354f3d18b039cf7a0558c1439d1`

---------

Signed-off-by: Jordan Mecom <jm@squareup.com>
Signed-off-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 10:32:41 -07:00
Tyler
8a7eb8d3d7 fix(agent): recover from unsupported image input instead of poisoning the turn (#4896)
## Problem

`buzz-dev-mcp` advertises `view_image` to every agent regardless of
whether the session's model accepts images. When a text-only model (e.g.
DeepSeek V4 Flash) takes the bait, the image lands in session history
and every subsequent LLM request 404s with `No endpoints found that
support image input`. The error was classified as `LlmModelNotFound` and
propagated fatally out of the turn loop — history stays poisoned,
buzz-acp retries the batch with exponential backoff, and the session
burns its entire clock doing no work. In a recent trial run, **all 57
trials that called `view_image` on a text-only model died this way; none
recovered.**

## Fix

Capability-gating the advertised tool isn't reliable — there is no
image-capability metadata at the agent layer across providers. Instead,
recover at the turn loop:

- **Typed error**: new `AgentError::UnsupportedImageInput`, classified
narrowly on the exact provider phrase `No endpoints found that support
image input` on both the generic 404 path and OpenRouter's 404 path.
Unknown-model 404s and OpenRouter parameter-routing 404s keep their
existing classifications. No deterministic retry.
- **In-turn recovery**: on this error, `RunCtx::run` strips every image
block from history — keeping the tool result (and therefore
tool-call/result pairing) intact — marks the result `is_error`, appends
actionable model-facing guidance ("The current model does not support
image input. The image was removed from conversation history so this
turn can continue. Use a text-based inspection tool…"), and continues
the same turn. Base64 never replays again.
- **Loop guard**: recovery only fires when at least one image was
removed; if the provider says "image" and history has none, the error
propagates as before.

## Tests

- Unit: phrase classification (typed, not retried; unknown-model 404
unaffected), idempotent image-to-error history mutation preserving call
IDs and text.
- End-to-end (`fake_llm.rs` + `fake_mcp.rs`): tool call → MCP image
result → 404 unsupported-image → same-turn recovery. Captured requests
prove round 2 carried the image, round 3 replays no image, carries the
guidance text, preserves pairing, and ends `end_turn`.
- Loop guard: typed unsupported-image error with **no** image in history
fails after exactly one provider request instead of spinning —
mutation-testing showed deleting the `removed == 0` guard survived the
suite, and `max_rounds` defaults to unlimited in production, so this
branch needed direct coverage.

Verified at `a210305019b33d5f56677b4c82bab79e4ac52d24`: `cargo test -p
buzz-agent` (full package, 381 unit + all integration suites) green;
`clippy --all-targets -D warnings` green; `fmt --check` green; pre-push
hooks (rust-tests, desktop-tauri-checks, branch-skew) green.

**Scope of the classification guarantee**: the classifier runs in the
shared `post()` (which Anthropic and OpenAI paths route through) and in
`openrouter_post()` — i.e., every 404 path in `llm.rs`. It only runs on
404 responses; providers that reject images with a different status
(e.g. a 400) are out of scope for this PR — see the review-comment
discussion for why broadening the phrase list alone would not cover
them.

Authored by Wren, loop-guard test by Sami, reviewed by Eva.

---------

Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
2026-08-05 11:51:05 -04:00
Wes
067c085f37 Define private managed agent wire protocol (#4593)
## Summary

- reserve kind `30179` for owner-private managed-agent aggregates
- define the fail-closed owner-self NIP-44 v2 envelope and versioned
payload codec
- bind runnable identity/configuration to complete signed
`30175`/`30177` recovery projections
- validate NIP-OA owner→agent attestations and reject self-attestation
- document NIP-PMA authority, migration prerequisites, privacy, and
deployment order
- keep generic relay ingest closed until private storage and atomic
aggregate CAS exist

## Safety boundary

This is the inert protocol/codec slice only. It does not publish
secrets, change agent authority, migrate local records, or enable kind
`30179` ingestion. The relay regression test proves generic EVENT ingest
still rejects the kind.

The finalized migration plan adds later prerequisites for relay-private
storage/CAS, runtime lease/fencing, Desktop cutover, and harness
authentication. Those belong in staged follow-up PRs rather than
expanding this inert foundation.

## Validation

At commit `67f0ea4ebb8d3ccba3a3eb9374e89a7178913f74`:

- `cargo test -p buzz-core` — 246 unit + 2 doc tests passed
- `cargo test -p buzz-relay
private_managed_agent_kind_remains_rejected_until_atomic_ingest_exists`
— passed
- push hooks: Rust tests and desktop checks passed (`2145` desktop tests
passed, `14` ignored)
- `cargo fmt --all -- --check`
- `git diff --check`

## Review

Princess Donut cleared security/data integrity with no remaining
high/medium findings. Mongo cleared migration compatibility and wire
grammar. The later runtime lease/fencing protocol was also adversarially
cleared as a plan; implementation slices still require independent
evidence before activation.

Deterministic plaintext/signed-projection/auth-tag interoperability
vectors remain a valuable follow-up, not an S0 merge gate; random NIP-44
ciphertext is intentionally not snapshotted.

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-08-05 08:34:02 -07:00
Tom Brow
dc17965c79 fix(mobile): serialize channel sections sync (#3165)
### What changed?

Serializes channel-section relay synchronization so a late relay
`CLOSED` cannot overlap an in-flight retry and install duplicate
subscriptions. Pending subscription results are invalidated and
immediately closed when the manager is disposed or superseded.

### Why?

The startup retry added in #3004 could race with a late `CLOSED` or
manager disposal, leaking an untracked live subscription. This keeps
retry recovery single-flight and makes the lifecycle boundary explicit.

### How is it tested?

Build and run.

Added tests:

-
[`ChannelSectionsManager`](https://github.com/block/buzz/tree/main/mobile/test/features/channels/channel_sections/channel_sections_manager_test.dart)
interleaving coverage for in-flight retry serialization and disposal
during subscription setup

*🤖 This PR was authored with a Buzz agent.*

Signed-off-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
Co-authored-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
2026-08-05 07:55:26 -07:00
Will Pfleger
6dbc946512 fix(desktop): make missing-command error actionable for released builds (#4802)
User-facing error for missing ACP harness commands has been pointing
released-build users to run `cargo build --release --workspace` and read
TESTING.md — both dead ends for anyone not building from source.

Updated message acknowledges that antivirus software can quarantine
bundled binaries and provides practical remediation steps. Preserves
pointer to TESTING.md for source builds.

Fixes issue context from #4491.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub16v54tttfqacx9ycvc3k0ut0npj564ahcuajzy6qjvh57ntmsf4uq4806j2 <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@buzz.block.builderlab.xyz>
2026-08-05 09:51:15 -04:00
Wes
8342dfcc58 chore(release): release Buzz Desktop version 0.5.5 (#4809)
## Buzz Desktop release v0.5.5

- **Frozen main:** `25a9cf1be6d245fbd7373cb1160dbc790baf5bd5`
- **Reviewed candidate:** `8380c1f8ead8816bcf1f4ea9f66aa08e2441b15a`
- **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>
desktop-v0.5.5
2026-08-04 16:54:16 -07:00
Kalvin C
25a9cf1be6 feat: paste composer text without formatting (#4801)
## Summary

- handle Cmd+Shift+V on macOS and Ctrl+Shift+V on Windows/Linux in the
message composer
- read plain text through the native Tauri/arboard clipboard path in
packaged builds, with a browser-only Clipboard API fallback
- re-enter ProseMirror's paste pipeline with populated `text/plain`
clipboard data so selection, undo, multiline behavior, and paste
observers remain intact
- cover both platform mappings with rendered composer E2E tests that
assert the native command path

## Testing

- `pnpm test` — 4,286 passed
- `pnpm check`
- `pnpm typecheck`
- `pnpm exec playwright test composer-selection-formatting.spec.ts
--project=smoke` — 26 passed
- `cargo check --manifest-path desktop/src-tauri/Cargo.toml --workspace
--all-targets --target aarch64-apple-darwin`
- `just desktop-tauri-test` — 2,206 core tests plus integration and
doc-test groups passed
- full pre-push hooks passed

## Manual verification

Physical packaged-app clipboard verification remains recommended on
macOS, Windows, and Linux. The automated E2E uses mocked Tauri IPC but
asserts the native `read_clipboard_text` command is invoked.

Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
2026-08-04 16:16:14 -07:00
Wes
79c52166cf Revert "chore(release): release Buzz Desktop version 0.5.5" (#4808)
Reverts block/buzz#4800
2026-08-04 16:09:33 -07:00
Wes
a0ed13de14 chore(release): release Buzz Desktop version 0.5.5 (#4800)
## Buzz Desktop release v0.5.5

- **Frozen main:** `4a2305170eef565bf1836e2859247e67c030f8af`
- **Reviewed candidate:** `2d03d37b05b68186b2caad9da79080032be3ac72`
- **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>
2026-08-04 16:00:29 -07:00