Merge upstream/main into the Dreamforge fork - #8
Merged
Conversation
## Summary - replace the two-option Type and Visibility dropdowns in the Create channel dialog with single-click segmented controls - keep Expires after as a dropdown and preserve the existing dropdown controls in edit and management dialogs - update channel creation end-to-end coverage for the direct controls ## Before Default Ongoing/Public state:  Type dropdown open, showing the extra selection click:  ## After Default Ongoing/Public state:  Temporary/Private state with the Expires after row visible:  ## Verification - Biome and TypeScript checks pass - 5,508 desktop unit tests pass - 88 channel smoke tests pass - source guards pass --------- Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ment) (block#6229) ## Why A wedged relay boot pod holding a relation lock can park every other writer in the fleet behind it: DB load pins at pool capacity in `Lock:relation` waits while CPU stays flat, and nothing server-side releases the lock until the holder dies. We hit exactly this in production — ~1,400 sessions queued behind one crash-looping pod's boot transaction for ~20 minutes until kubelet killed the container. ## What Applies session-level Postgres timeouts to every **writer** connection inside the existing single `after_connect` hook in `buzz-db`, all env-tunable through the same `Config::from_env → DbConfig` path as the existing pool-size knobs: | Env var | GUC | Default | Effect | |---|---|---|---| | `BUZZ_DB_LOCK_TIMEOUT_MS` | `lock_timeout` | 5000 | statements waiting on any lock fail fast instead of parking behind a wedged holder | | `BUZZ_DB_IDLE_TXN_TIMEOUT_MS` | `idle_in_transaction_session_timeout` | 60000 | reaps wedged clients idling inside an open transaction while holding locks | | `BUZZ_DB_STATEMENT_TIMEOUT_MS` | `statement_timeout` | 0 (off) | opt-in runaway-statement cap; off by default because startup migrations/backfills legitimately run long statements | `0` disables a timeout (Postgres semantics) and deliberately passes through the env parsing — unlike the pool-size knobs where `0` falls back to the default. The reader pool is untouched: replica sessions never take contended locks and already fail acquire in 150 ms. Deployers tune these via plain env vars (`.env`, or `relay.extraEnv` in the Helm chart) — no code changes needed. ## Behavior change to note With the 5 s default `lock_timeout`, a boot-time migration or backfill that waits >5 s on a lock now errors (surfacing in logs / crash-looping the pod) instead of stalling silently. That is the intended visible-failure-over-fleet-stall tradeoff; deployers with slow contended migrations can set `BUZZ_DB_LOCK_TIMEOUT_MS=0`. ## Testing - `cargo test -p buzz-db -p buzz-relay` — buzz-db green; buzz-relay has 9 failures that also fail on clean `main` in this environment (api::admin/api::media/mesh_demo — unrelated, pre-existing). - New config test covers override / `0`-passthrough / invalid-fallback for all three env vars. - Extended the existing `writer_pool_safety_hook_is_single_and_composed` source-shape test so the timeouts can't drift out of the single `after_connect` hook (SQLx replaces hooks — a second hook would silently disarm the floor guard). - `cargo fmt --check` and `cargo clippy --all-targets` clean for the touched crates. Closest existing PR/issue: none found. --- **Update Aug 28, 17:06 EDT:** Rebased onto `main` at `a3730784fc` and addressed the latest correctness review. - Ported the timeout policy onto the refactored `buzz-db::runtime` pool constructor and kept the shared env overlay for relay, admin, deletion, and audit writers. - Migration/schema-destruction connections now disable `lock_timeout` and `statement_timeout` for their intentional long wait/DDL path. This supersedes the earlier “Behavior change to note”: contended boot migrations wait for the current migration owner rather than crash-looping after five seconds. - The audit worker now preserves and retries the same entry on PostgreSQL `55P03` lock timeouts, using exponential backoff capped at one second. Other database errors retain the existing terminal error behavior, and retries emit `buzz_audit_log_lock_retries_total`. - Added CI-backed PostgreSQL regressions for writer GUC installation/migration exemption, audit-pool lock timeouts, and worker recovery. The worker regression holds the real audit advisory lock past `lock_timeout`, observes a retry, releases the lock, and proves the original entry is appended exactly once. Current verification supersedes the earlier testing notes: workspace Rust clippy passed with warnings denied; all nine infrastructure-free backend unit-test lanes passed; all three focused PostgreSQL regressions passed against PostgreSQL 17; formatting, diff checks, file-size guards, and desktop frontend checks passed. The Linux Blox workstation could not run the unrelated Tauri native lane because `glib-2.0` is absent, so that platform check is left to PR CI. --- **Update Aug 31, 11:09 EDT:** Rebased onto current `main` at `c3132c3ee9` and reran the requested audit-lock contention scenario on Blox at head `896c3fe9ed`. - `git range-diff` reports both PR commits unchanged by the rebase; the branch remains two commits and the worktree is clean. - `cargo fmt --all -- --check` and clippy with warnings denied passed for `buzz-db`, `buzz-relay`, `buzz-admin`, and `buzz-deletion`. - All three focused PostgreSQL 17 regressions passed: writer session timeout/migration exemption, audit writer timeout bounds, and audit worker recovery of the original entry exactly once. - Live protocol verification used a head-built relay and CLI, native PostgreSQL 17/Redis, `BUZZ_DB_LOCK_TIMEOUT_MS=300`, and an eight-second hold on the community audit advisory lock. The real message was accepted and persisted once while the lock was held; its audit-row count remained zero during contention while retries accumulated. After release, exactly one `event_created` audit row appeared and remained exactly one after an additional two-second duplicate check. The run recorded nine lock-timeout retries, zero audit failures, and event ID `7f8c4ffae28e78555fcf2d56396d6e6c01b3712e5411288dc79e9a54af9d9444`. Generated with Codex --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com>
NIP-OA relay admission now evaluates signed `created_at<` and `created_at>` conditions against the already verified authentication event timestamp. An owner-signed credential such as `created_at<1` no longer upgrades its holder to relay membership. The verified timestamp now flows from NIP-42, NIP-98, and Blossom authentication through the shared membership gate used by WebSocket, HTTP, Git, media, huddles, GIF, and workflow requests. Owner-attested access fails closed when no signed authentication timestamp is available. Direct relay members keep their existing admission behavior, and `kind=` remains connection-level metadata as specified by NIP-AA. Tests cover strict time-bound evaluation, missing timestamp rejection, and HTTP authentication timestamp propagation. Testing: - `cargo test -p buzz-sdk nip_oa::tests -- --nocapture` - `cargo test -p buzz-relay api::relay_members::tests -- --nocapture` - `cargo test -p buzz-relay api::bridge::tests -- --nocapture` - `just ci` --------- Signed-off-by: Jordan Mecom <jm@squareup.com>
## Why Evaluating buzz agent memory retrieval by seeding a memory then asking the buzz agent a question it needs that memory. **Bug Found**: System prompt had no inclusion of retrieving cold memories and suggested looking in a mem/*.md directory that does not exist. Updated `system-prompt.md` to include memory CLI tools and usage. Eval Before System Prompt Change: 0/3 Eval After System Prompt Change: 3/3 ## What - Add a `memory-retrieval` benchmark that seeds agent memory with `buzz mem set` before asking a direct question. - Grade the observable threaded answer without inspecting tool calls or exposing the answer in channel history. - Teach agents to use `buzz mem set`, `buzz mem ls`, and `buzz mem get` for cold memory. - Add a wire-debug endpoint configuration for diagnosing ACP tool calls in local runs. - Add fixture, seeding, verifier, and prompt coverage. ## Risk Assessment Low. The runtime changes are limited to the benchmark harness. The production-facing change clarifies existing memory commands in the base prompt; it does not change memory storage, relay behavior, or authorization. ## References - Before the system-prompt changes, 0/3 attempts passed because agents never invoked the `buzz mem` CLI and instead searched a non existent filesystem - After the changes, 3/3 attempts passed. ACP wire logs confirmed that every agent ran `buzz mem ls` followed by `buzz mem get` and returned `net_gpv`. --------- Signed-off-by: Philip Azar <pazar@squareup.com>
Codex CLI can leave a PTY descendant holding the action's inherited
stdio after the turn completes. The `runCodexExec.ts` wrapper waits on a
`close` event that never fires, so the `Review pull request` step hangs
until the job timeout kills it — discarding the finished review the CLI
already wrote to disk.
The CLI writes the completed review to the `--output-last-message` file
(exposed as `output-file`) **before** the hang. This PR adds a salvage
step that recovers it, and sets the step and job timeouts to preserve
the full 30-minute Codex execution budget.
**Changes (`codex-security-review.yml`):**
- Add `output-file: ${{ runner.temp }}/codex-review.json` to the `Review
pull request` step so the CLI writes the result before the hang.
(`runner` context is valid in `steps.with`; not in `jobs.env`.)
- Add `timeout-minutes: 30` and `continue-on-error: true` to the Codex
step — a hang now costs ≤30 minutes instead of 40, and the salvage step
still runs.
- Set job `timeout-minutes: 40` to give setup, step cancellation, and
salvage sufficient headroom without colliding with the Codex execution
budget. The original 30-minute job timeout was too narrow: evidence from
run
[33114428326](https://github.com/block/buzz/actions/runs/33114428326/job/98665369165)
shows completed output appearing 28m46s after step start, meaning a
20-minute step timeout could kill a legitimate review before the salvage
file exists.
- Add a `Salvage review output` step with `if: always()`: prefers
`steps.run_codex.outputs.final-message` on a clean exit; falls back to
the output file when the step timed out. The output file path is set in
the step's own `env` block (`CODEX_OUTPUT_FILE: ${{ runner.temp
}}/codex-review.json`), where `runner` is valid. Validates shape
(non-empty JSON object, has `overall_risk`); fails the job hard if
neither source is present.
- Wire the job `outputs.review_json` to
`steps.salvage.outputs.review_json`.
**Changes (`Justfile`, `ci.yml`):**
- Add `actionlint .github/workflows/codex-security-review.yml` to
`security-review-check` so expression-validity errors are caught
locally.
- Provision `actionlint` via Hermit (pinned v1.7.12) rather than a
one-off `Install actionlint` curl step, so the same binary is used
locally and in CI.
**Security posture is unchanged:** the salvage step reads the action's
own output and a file written to `runner.temp` — neither is
PR-controlled. Credential-stripping env block on the Codex step is
untouched.
Note this is a temporary workaround until
openai/codex-action#169 is addressed
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary - render every agent/AI identity as a 30% squircle across desktop and mobile while keeping human avatars circular - propagate agent identity through message, thread, profile, reaction, member, DM, search, workflow, project, huddle, forum, pulse, and agent-management surfaces - preserve squircle geometry for fallbacks, focus/status treatments, add-agent controls, and overlapping avatar outlines (`calc(30% + 2px)` for the outer background) ### Related issue None found. This change was requested and visually reviewed in the originating Buzz thread. ### Testing - `just desktop-test` — 5,799 passed - `just mobile-test` — 2,008 passed - pre-push gates passed at `0d59d77b120dcb90aac2f918e422c11c9fa5353b`: desktop check, TypeScript typecheck, desktop full test suite, mobile format/analyze and full test suite, Rust tests, Tauri checks, and differential file-size gate - deterministic desktop visual sweep covered channel messages/thread summaries; thread, subthread, and sub-subthread depths; reactions and reactor popovers; hover/full profiles; added-to-channel activity; channel members/settings; agent library/team overlaps; agent creation; mention autocomplete; and DM header/sidebar/settings ### UI evidence The complete labeled visual matrix is available in the originating Buzz review thread. GitHub-hosted copies will be added in a follow-up PR comment using the repository screenshot script. --------- Signed-off-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz> Co-authored-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz>
> Pinky, an AI agent, is opening this PR on Wes's behalf.
## Summary
Workflow-generated messages can contain a valid agent mention but still
fail the ACP inbound author gate because the relay signs the event. This
keeps the existing wake policy and gives ACP a narrowly verified
effective author:
- preserve the workflow owner's existing `p` tag and all
rendered-mention `p` tags
- add explicit `["buzz:workflow-owner", <owner hex>]` provenance to
relay-generated workflow messages
- add `["buzz:workflow-mention", <agent hex>]` authority only for
mentions resolved from the stored, unrendered workflow step template
- accept that owner only for a verified kind-9 event signed by the
relay's current NIP-11 `self` key, with unique canonical workflow
metadata and an explicit workflow mention for the receiving agent
- route the verified owner through the existing author and in-flight
mode policies in both normal and setup listeners
- refresh relay identity after reconnects, retaining the last verified
key on transient fetch errors while treating a successful response
without `self` as definitive removal
Malformed, duplicate, forged, tampered, wrong-kind, and wrong-relay
attribution all fail closed to the raw event signer. `respond-to=nobody`
remains absolute. Old/mixed-version messages without the explicit
provenance retain their current fail-closed behavior.
## Trust boundary
The workflow owner means **“scheduled by,” not “authored every rendered
word.”** Trigger-controlled substitutions may still produce ordinary `p`
mention routing for compatibility, but they cannot mint
`buzz:workflow-mention` authority. Only a target named in the durable
owner-authored step template can receive that authority.
The author gate is not bypassed: after relay signature/provenance
verification, the effective owner is evaluated under the same
`owner-only`, `allowlist`, DM, and `nobody` policies used for ordinary
messages. Owner control commands continue to use the raw event signer.
## Why this PR
This is the focused immediate fix for waking an **online** agent from a
stored workflow mention. Earlier attempts were not a finished mergeable
fix and had materially different or incomplete trust designs. Larry's
larger draft stack addresses durable delivery across restarts; that
remains valuable future work and can supersede this effective-author
path when it lands.
## Validation
At exact clean commit `fe5b55619fe44176343eefb4cb7fe180df45a7d8`:
- `buzz-relay workflow_sink`: 25/25 passed, including all four ignored
PostgreSQL cases
- `buzz-acp --lib`: 845/845 passed
- `buzz-workflow --lib`: 169/169 passed (2 unrelated PostgreSQL tests
ignored)
- warnings-denied Clippy passed for the changed Rust packages
- `cargo fmt --all -- --check` passed
- `git diff --check` passed
- repository pre-push gates passed, including branch-scoped Rust tests
- CI now selects the ACP library tests and the relay's pure + PostgreSQL
workflow-sink tests so these guards cannot silently remain unexecuted
The production event-to-author gate is shared by normal and setup
listeners and has biting regression tests for accepted explicit
attribution, legacy owner-`p` rejection, and forged-attribution
rejection.
## Exact-head local relay + ACP proof
Following the release-binary/local-relay shape in `TESTING.md`, the
exact commit above passed a fresh isolated real-process matrix using:
- a freshly recreated Postgres database with migrations
- isolated Redis
- exact-head release `buzz-relay`, `buzz`, `buzz-admin`, and `buzz-acp`
binaries
- newly provisioned owner, channel, and bot member through the CLI
- workflow creation and triggering through the running relay
- a deterministic ACP protocol subprocess capturing actual
`session/prompt` dispatches
- a NIP-11 `self` value verified against the running relay signer
Cases:
1. A stored explicit workflow mention woke an `owner-only` agent exactly
once.
2. A workflow message without an agent mention did not wake it.
3. A non-relay signer forging every workflow authority tag did not wake
it.
4. Trigger-controlled `{{trigger.text}}` containing `@Wake Agent`
retained ordinary `p` routing but received no authority-bearing
workflow-mention tag and did not wake the agent.
5. `respond-to=nobody` remained absolute for a valid relay-authenticated
workflow mention.
The deterministic ACP subprocess isolates and directly proves relay →
ACP authorization and prompt dispatch without depending on external
model behavior.
## Deployment and residual risk
Relay and ACP changes must be deployed together for the new wake
behavior; mixed versions fail closed. Production paired-deployment proof
remains distinct from the successful local integration run. Setup-mode
behavior has automated coverage but was not a separate case in the
five-case local matrix. Relay-key rotation is observed at ACP
startup/reconnect; transient NIP-11 errors retain the last verified key,
an intentional availability tradeoff documented in code.
---------
Signed-off-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz>
Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz>
Co-authored-by: LioLionel <62820906+LioLionel@users.noreply.github.com>
Co-authored-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
Co-authored-by: Carl <9d00794d3df50972eb8b615511783cab12a77a8fd5dd5edd58073ec73b54bd8b@buzz.block.builderlab.xyz>
…6961) Pinky, an AI agent, updated this description on Wes's behalf after taking over the startup investigation. **Category:** fix **User Impact:** An EVENT refused by WebSocket admission or handler saturation receives a correlated `OK(event_id, false, reason)` instead of an uncorrelated NOTICE, so the client can settle that refusal without waiting for its publish timeout. Rate-limited refusals also arm client backoff. This fixes a protocol failure mechanism; it does not establish that every startup send will succeed or that the reported Desktop startup incident is fully resolved. **Problem:** Startup opens several live subscriptions and publishes at once, and the relay's WebSocket admission gate is a fixed 5-second window (`ws_admission_budget` = `human_ws_events_per_sec * 5`). If that shared per-principal quota is exhausted, `enforce_ws_admission` previously rejected an EVENT with a bare `["NOTICE", reason]`. Quota pressure is a possible trigger, not proof of the original incident's complete cause. A NOTICE carries no event id. Both clients settle a pending publish *only* from an `OK` keyed by event id (desktop `pendingEvents`, mobile `_pendingEvents`), so nothing settled — and `handle_text_message` returns early, so no `OK` ever followed either. The send **could not fail**; it could only time out at `PUBLISH_TIMEOUT_MS` = 25s. That explains how this rejection mechanism can produce a roughly 25-second timeout; attributing the original report to it still requires the actual startup/send workflow. The handler-semaphore saturation path had the identical defect, and that one needs no quota burst to fire. **Solution:** NIP-01 gives each request type its own acknowledgement channel, and a rejection is only actionable on the same one. Reject a REQ with `CLOSED`, an EVENT with `OK(id, false, reason)`, and fall back to `NOTICE` only where no per-request correlation exists. COUNT refusals now also use `CLOSED(query_id, reason)` per NIP-45, covering both quota admission and handler saturation (added in `cd12c93804b87a24b61075dfd171dc471a0a527f`). Reason strings are unchanged, so the `rate-limited:` prefix and `retry in {N}s` hint that existing client gates parse keep working (desktop `parseRateLimitHint`, mobile `RelayRateLimitGate`, buzz-acp `set_rate_limit_gate`). Only the frame *type* changes, so `docs/multi-tenant-relay.md` L7 stays satisfied. Two notes on how this landed, both worth a reviewer's attention: 1. **A survived mutation became a design change.** `send_admission_result` originally took a `RejectionTarget` parameter, and reverting the *second* call site (the per-minute message quota) survived the whole suite — with Redis unreachable the first quota check short-circuits, so that line is unreachable in test. Rather than test around it, the parameter is gone: the target is derived from the frame, so no call site can name the wrong channel. 2. **The relay fix would have caused a client regression on its own.** Gate arming lived only in the NOTICE branch. Once rejections arrive as `OK:false`, `handleOk` failed the send without ever backing off — the client would retry straight into the same quota. Desktop and Mobile now arm on a `rate-limited:` OK rejection. ACP was subsequently fixed in `3b06dd32493596ec650f20abf8805791c50fdc24`: it arms the gate and re-parks only the refused observer frame, preserving other in-flight frames. Desktop gets `activateRateLimitIfSignalled` as the single owner of that prefix test, called from both `handleOk` and the NOTICE branch. <details> <summary>File changes</summary> **crates/buzz-relay/src/rejection.rs** (new) Owns the admission-rejection concern: `RejectionTarget`, `rejection_target_for`, `request_rejection_message`, `send_admission_result`, and `enforce_ws_admission`, moved out of `connection.rs`. Six tests, two of which drive the real `enforce_ws_admission` against a real `AppState`. **crates/buzz-relay/src/connection.rs** Fix the EVENT handler-semaphore rejection to correlate to the event id; delegate admission to the new module. Add two tests that drive the real `handle_text_message` with every handler permit held. Down from 1319 to 1116 lines. **crates/buzz-relay/src/state.rs** Widen the existing `test_state` helper to `pub(crate)` so the rejection tests reuse it rather than adding a ninth copy of `AppState` construction. **desktop/src/shared/api/relayRateLimitGate.ts** Add `activateRateLimitIfSignalled` — one owner for the `rate-limited:` prefix test, since three inbound frame types now carry it. **desktop/src/shared/api/relayClientSession.ts** Arm the gate on a rate-limited OK rejection; route the NOTICE branch through the same helper. Net zero lines, which keeps this already-oversized file within the differential ratchet. **desktop/src/shared/api/relayClientPublishRejection.test.mjs** (new) Four tests against the real `RelayClient`: a rate-limited OK settles the pending publish and arms the gate; an ordinary rejection does not arm it; an accepted OK still resolves. **mobile/lib/shared/relay/relay_session.dart** Arm the gate in `_handleOk` for a rate-limited rejection. **mobile/test/shared/relay/relay_session_test.dart** Two tests driving the real `publish` + `debugHandleMessage` path. </details> <details> <summary>Validation</summary> **Mutation-tested — 5 mutations, all now killed.** Each production call site was reverted to the defective behaviour to confirm a test fails. This caught two false-negative tests: | # | Mutation | Result | |---|----------|--------| | 1 | `rejection_target_for`: EVENT → `Connection` | 4 tests fail | | 2 | EVENT handler-semaphore call site → bare NOTICE | **survived at first** | | 3 | per-minute quota call site → `Connection` | **survived**; fixed by removing the parameter | | 4 | desktop `handleOk` gate arming removed | 1 test fails | | 5 | mobile `_handleOk` gate arming removed | 1 test fails | Mutation 2 is the lesson: my first saturation test called `request_rejection_message` directly, so reverting the real call site inside the `match` arm left it green. It now drives `handle_text_message` itself and dies on that mutation. - `cargo test -p buzz-relay` — 928 passed, 1 failed: `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo`, **pre-existing**, reproduced with all changes stashed at `4dd4d73de`. - `cd desktop && npm test` — 5721 passed, 0 failed (full suite). - `cd mobile && flutter test` — 1876 passed, 0 failed (full suite). - `just fmt-check`, `just clippy`, `just desktop-check`, `just mobile-check`, `just file-size-check` — clean. Desktop's 5 biome warnings are pre-existing (reproduced with changes stashed). - All 9 pre-push lanes green, including `rust-tests` and `desktop-tauri-checks`. **Not verified:** not reproduced end-to-end against a live relay under a forced quota burst. The causal chain is source-proven and mutation-proven at the frame level; the ~25s attribution follows from `PUBLISH_TIMEOUT_MS` but is not directly measured. A packaged-build click-through would close that gap. </details> Related work: block#6957 bounds Desktop HTTP event submission, but safe retained-operation recovery after exhausted/ambiguous outcomes remains unfinished. block#6998 is the separately reviewable Desktop readiness/duplicate-subscription slice. Neither is claimed to complete native before/after startup-send validation. Diagnosis note: `RESEARCH/DESKTOP_STARTUP_SEND_STALL_2026_08_27.md` (Brain's workspace). ## Current review disposition (2026-08-28) The [review on `cd12c938`](block#6961 (review)) identified ACP's missing rate-limited-OK handling. Commit `3b06dd32493596ec650f20abf8805791c50fdc24` fixes gate arming, re-parking the specifically refused observer frame, and the stale NOTICE comment. Two regressions drive the real frame dispatcher. See [the implementation and validation response](block#6961 (comment)). The Mobile generation-check inline thread is resolved: its `async publish` returns a failed Future when superseded; it does not throw synchronously at invocation. No further production change was indicated by that comment. The validation counts above describe the original slice, not a new rerun. At `3b06dd324`, the current GitHub check rollup has successful completed test/build checks (non-applicable jobs skipped). The security-review comment still requires review for the current base/head range; do not read a green authorization job as a completed security review. Approval and merge remain human decisions. --------- Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz> Co-authored-by: Carl <9d00794d3df50972eb8b615511783cab12a77a8fd5dd5edd58073ec73b54bd8b@buzz.block.builderlab.xyz>
## Summary Introduces a protected-build boundary for the default-off Bestie experiment without adding any Bestie product surface. - Official OSS builds select an empty protected-feature module and emit no Bestie/Chief metadata or implementation content. - Protected internal builds select a separate module graph containing the Bestie experiment definition. - Within an internal build, Bestie remains disabled until the user opts in under Settings → Experiments. - The production build runs an artifact matrix and fails if OSS output contains protected content or internal output lacks the Bestie manifest. ## Build contract | Build variant | User opt-in | Result | | --- | --- | --- | | Official OSS | Any/forged | Bestie absent from the compiled artifact | | Protected internal | Off | Bestie available but disabled | | Protected internal | On | Bestie enabled | The companion protected-release change is squareup/buzz-releases#91. It sets `VITE_BUZZ_BESTIE=1`, requires that exact value, forwards it into the signed macOS build, and asserts the contract in release validation. ## Why this is separate This gives later Bestie PRs one build-selected import seam. Protected implementations must be reachable only from the internal module so they never enter the official OSS module graph. ## Non-goals - No Bestie persona or provisioning - No sidebar, app-chrome, or message-toolbar UI - No entitlement or secrecy claim: the source is public; this boundary controls official Block artifacts ## Verification - Exact commit `523cf49ced03cba9be43836a54d6aa5d6923cc82` - Full `just ci`: 5,673 Desktop tests, 2,773 Tauri tests, 1,860 mobile tests, Rust/Tauri/web/mobile static checks and builds - OSS production artifact: scanner confirms no `Bestie`, `Chief of Staff`, or `builtin:bestie` content - Internal production artifact: scanner confirms the protected Bestie manifest is emitted - Both build orders verified; `dist` retains the requested variant for Vite/Tauri packaging --------- Signed-off-by: Arjun Mahanti <arjun@squareup.com> Signed-off-by: Fizz <fizz@buzz.local> Signed-off-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Fizz <fizz@buzz.local> Co-authored-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz>
**Category:** new-feature **User Impact:** People can add a short public description to an agent and see what it does directly on agent cards and profiles. **Problem:** Agent cards previously showed only a model label, so people had to open an agent and inspect its instructions to understand its purpose. Public metadata also needed one trustworthy lifecycle across local edits, relay catalogs, profiles, and portable snapshots. **Solution:** Add an optional owner-authored description with a 280-character visible-text policy, publish it as profile `about`, and prefer it on agent cards while retaining the model fallback. Description metadata is excluded from the spawn-content hash, remains definition-owned, and is validated independently at every untrusted or persistence boundary. <details> <summary>File changes</summary> **desktop/src-tauri/src/commands/agent_config_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/agent_discovery/relay_directory.rs** Updates relay-directory profile test publication for the expanded profile contract. **desktop/src-tauri/src/commands/agent_models_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/agent_models_update.rs** Preserves the effective `about` value when instance edits republish a complete profile event. **desktop/src-tauri/src/commands/agents.rs** Carries the effective authored description into initial managed-agent profile publication. **desktop/src-tauri/src/commands/agents_profile.rs** Adds `about` to profile reconciliation and keeps description, name, and avatar synchronized against relay state. **desktop/src-tauri/src/commands/agents_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/personas/card.rs** Materializes the definition-owned description before minting a portable agent card snapshot. **desktop/src-tauri/src/commands/personas/create.rs** Normalizes and validates raw authored descriptions before persona persistence. **desktop/src-tauri/src/commands/personas/delete_cascade_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/personas/inbound.rs** Validates descriptions at inbound relay ingress and applies accepted values to local definitions. **desktop/src-tauri/src/commands/personas/inbound/catalog_reconcile_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/personas/inbound/inbound_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/personas/mod.rs** Centralizes raw-byte validation followed by trim/empty normalization for description writes. **desktop/src-tauri/src/commands/personas/pending.rs** Revalidates descriptions before preparing public persona publications. **desktop/src-tauri/src/commands/personas/sharing.rs** Carries the optional public description through this managed-agent compatibility path. **desktop/src-tauri/src/commands/personas/snapshot.rs** Materializes definition-owned descriptions into portable instance snapshots without creating a second persisted authority. **desktop/src-tauri/src/commands/personas/snapshot/fidelity_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/personas/snapshot/import.rs** Restores snapshot descriptions onto imported definitions while keeping linked instance copies absent. **desktop/src-tauri/src/commands/personas/snapshot/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/personas/update.rs** Persists persona description edits, republishes linked profiles, and preserves legacy avatars during complete kind:0 replacements. **desktop/src-tauri/src/commands/personas/update/name_propagation_tests.rs** Proves description-only profile sync does not write instance state or clear a legacy avatar. **desktop/src-tauri/src/commands/team_snapshot.rs** Round-trips member descriptions through team snapshots and imported definitions. **desktop/src-tauri/src/commands/team_snapshot/tests.rs** Covers team member description export and import fidelity. **desktop/src-tauri/src/commands/teams/adopt/apply.rs** Starts adopted team catalog members without synthesizing an unauthored description. **desktop/src-tauri/src/commands/teams/adopt/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/teams/pending/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/commands/teams/sharing/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/egress_guard_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/event_sync_team_catalog_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/agent_description.rs** Defines the canonical Rust description resolution used by profile publication and reconciliation. **desktop/src-tauri/src/managed_agents/agent_events.rs** Updates managed-agent record construction for the optional public description field. **desktop/src-tauri/src/managed_agents/agent_snapshot.rs** Includes descriptions as snapshot profile `about` metadata and validates them at decode ingress. **desktop/src-tauri/src/managed_agents/agent_snapshot_envelope.rs** Updates managed-agent record construction for the optional public description field. **desktop/src-tauri/src/managed_agents/agent_snapshot_tests.rs** Covers snapshot description export and rejection of unsafe or overlong imported metadata. **desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/definition_validation.rs** Adds the shared 280-character visible-text policy for public descriptions. **desktop/src-tauri/src/managed_agents/discovery/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/effective_config/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/global_config/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/mod.rs** Exports the description resolution and validation helpers to managed-agent consumers. **desktop/src-tauri/src/managed_agents/nest/render_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/parallelism.rs** Updates managed-agent fixtures for the optional description field without changing runtime configuration behavior. **desktop/src-tauri/src/managed_agents/persona_events.rs** Adds description to persona event content while deliberately excluding it from the spawn-relevant content hash. **desktop/src-tauri/src/managed_agents/persona_events/tests.rs** Pins description event round-tripping and proves description-only edits do not change the restart hash. **desktop/src-tauri/src/managed_agents/personas.rs** Initializes built-in persona records without authored descriptions for backward-compatible defaults. **desktop/src-tauri/src/managed_agents/personas/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/readiness.rs** Updates managed-agent fixtures for the optional description field without changing runtime configuration behavior. **desktop/src-tauri/src/managed_agents/restore.rs** Includes the effective description in launch-time profile reconciliation. **desktop/src-tauri/src/managed_agents/runtime/test_fixtures.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/runtime/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/spawn_snapshot/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/team_catalog/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/team_snapshot.rs** Updates managed-agent record construction for the optional public description field. **desktop/src-tauri/src/managed_agents/teams_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/managed_agents/types.rs** Adds optional description metadata to persona and managed-agent records and their compatibility projections. **desktop/src-tauri/src/managed_agents/types/requests.rs** Accepts optional descriptions on persona create and update IPC requests. **desktop/src-tauri/src/managed_agents/types/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/migration_avatar_tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src-tauri/src/persona_catalog.rs** Parses and validates descriptions at the untrusted community-catalog boundary. **desktop/src-tauri/src/persona_catalog_tests.rs** Covers valid catalog descriptions plus rejection of malformed, invisible, and overlong values. **desktop/src-tauri/src/relay.rs** Publishes and queries kind:0 `about` so relay profiles preserve authored descriptions. **desktop/src-tauri/src/relay/tests.rs** Updates managed-agent/persona fixtures for the optional description field while preserving the behavior under test. **desktop/src/features/agents/AGENTS.md** Documents description ownership, validation, snapshot, hashing, and display invariants for future changes. **desktop/src/features/agents/lib/agentDescription.test.mjs** Pins Unicode counting, paste clamping, trimming, and empty authored-description behavior. **desktop/src/features/agents/lib/agentDescription.ts** Provides shared display resolution, Unicode-scalar counting, and paste clamping for descriptions. **desktop/src/features/agents/lib/personaCatalogRelay.ts** Maps validated catalog descriptions into catalog persona projections. **desktop/src/features/agents/ui/AgentDefinitionDialog.tsx** Adds the description draft to create and edit submission while extracting identity fields from the large dialog. **desktop/src/features/agents/ui/AgentDescriptionField.tsx** Renders the public description input, helper copy, and Unicode-aware near-limit counter. **desktop/src/features/agents/ui/AgentIdentityCard.tsx** Generalizes the card second line to show a two-line description or the existing model fallback. **desktop/src/features/agents/ui/UnifiedAgentsSection.tsx** Prefers authored descriptions on persona cards and retains model labels when no description exists. **desktop/src/features/agents/ui/personaDialogState.test.mjs** Verifies edit and duplicate drafts preserve authored descriptions. **desktop/src/features/agents/ui/personaDialogState.ts** Seeds authored descriptions into edit and duplicate dialog drafts. **desktop/src/features/agents/ui/usePersonaActions.ts** Preserves descriptions when copying catalog personas into local definitions. **desktop/src/shared/api/personaTypes.ts** Defines description-bearing persona wire types in a focused module split from the size-constrained API type file. **desktop/src/shared/api/tauriPersonas.test.mjs** Verifies raw persona descriptions map into the frontend model and absent values become null. **desktop/src/shared/api/tauriPersonas.ts** Maps description fields across Tauri and preserves raw authored bytes for authoritative Rust validation. **desktop/src/shared/api/types.ts** Re-exports the extracted persona types without changing consumer import paths. **desktop/src/testing/e2eBridge.ts** Extends mock persona create, update, publication, and catalog parsing with production-shaped description behavior. **desktop/tests/e2e/agents.spec.ts** Verifies an edited description persists and appears on the agent card. </details> ### Reproduction Steps 1. Open **Agents**, edit a custom or built-in agent, and enter a sentence in **Description**. 2. Save the agent and confirm the sentence appears as the second line on its card. 3. Reopen the agent and confirm the authored description is restored; clear it and confirm the card returns to the model label. 4. Paste more than 280 Unicode characters and confirm the field keeps the first 280 characters and shows the near-limit counter. 5. Share or export/import the agent and confirm the description survives in the catalog/profile or snapshot without showing a restart-required badge for a description-only edit. ### Screenshots / Demo The focused Playwright flow `built-in persona edits persist` exercises the edited dialog, persisted value, and resulting card subtitle. Screenshots can be added after review if the field placement or two-line card treatment needs visual iteration. ### Verification - `cargo test --manifest-path desktop/src-tauri/Cargo.toml --lib` — 3,029 passed - `cd desktop && pnpm test` — 5,805 passed - `cd desktop && pnpm exec tsc --noEmit` - Focused Playwright: `built-in persona edits persist` — passed - Pre-push desktop, Tauri, typecheck, test, file-size, and branch-skew gates — passed --------- Signed-off-by: tulsi <tulsi@block.xyz>
## Summary - render an auxiliary panel's requested header backdrop in docked/split mode - preserve explicit transparent-backdrop behavior - cover a populated, scrolled thread pane so timeline content cannot bleed through its header ## Root cause `RightAuxiliaryPane` correctly paints above the channel's shared header backdrop so close/edit controls remain visible. The docked `AuxiliaryPanelHeader` branch, however, ignored its `backdrop` request, leaving scrolled thread content in that higher stacking context unbacked. ## Verification - desktop unit suite: 5,801 passed - desktop TypeScript: passed - Biome checks: passed (existing unrelated repository warnings only in the earlier full run) - targeted Playwright scroll regression: passed - ultrawide thread-pane Playwright coverage: passed Signed-off-by: Wintermute <c0fc581234c3585602139eec347ced7b82af65b6f6c10728348515c0c06c51c3@buzz.block.builderlab.xyz> Co-authored-by: Wintermute <c0fc581234c3585602139eec347ced7b82af65b6f6c10728348515c0c06c51c3@buzz.block.builderlab.xyz>
…block#7061) Mining the last 25 PRs' review threads (45 substantive findings, 11 reviewed PRs, avg **4.8 review rounds** each) shows **53% of findings are repeats** of five clusters: swallowed failures, stale-async-state races, tests that don't bind the production seam, unbounded resources/retry loops, and non-atomic multi-step persistence. PR block#6956 alone burned 4 rounds converging on one of these classes. A second, independent mining pass over **71 agent-review rooms (303 findings, Aug 18–29)** confirmed the same clusters and added outcome data — how often authors actually fix each finding class once flagged: test-seam binding and unbounded-resource findings **100%**, swallowed errors **90%**, stale-state races **70%**. It also surfaced two clusters the GitHub-thread pass under-sampled: **assistive-semantics defects** (44 findings, second-largest cluster) and **input-modality divergence** (27 findings), now rules 7–8. This PR distills those clusters into eight imperative rules in AGENTS.md so agents apply them **before writing code**, adds one client-consumption invariant to ARCHITECTURE.md §5, and places the test-quality rule in TESTING.md (per the team decision that testing docs are the canonical guide for review standards), cross-referenced from AGENTS.md. Each rule cites the PRs where it was litigated. Raw mining data: `reviews.jsonl` / `comments.jsonl` + `backfill/buzz-review-findings.jsonl` (review-mining artifacts, not committed). No code changes. CLAUDE.md is a symlink to AGENTS.md and picks this up automatically. 🤖 Drafted by Jude's agent from automated mining of this repo's last 25 PRs' review threads and 71 agent-review rooms; every rule cites the PRs where it was litigated. Jude reviews and owns the result. Mining method + raw cluster data available on request. --------- Signed-off-by: Jude Edwards <judeedwards@squareup.com>
…#6732) ## What this does In a channel, people often run several unrelated conversations at once (separate threads). Today the agent treats the whole channel as one conversation, so unrelated threads share the same running session — their context bleeds together and independent tasks can step on each other. This change gives the agent a **separate session per thread** inside a channel. Direct messages stay as one conversation (unchanged). The channel is still the boundary for who is allowed in and what is visible — only the agent's working context is now split by thread. ## How it is turned on Off by default. Operators opt in with one setting: - `BUZZ_ACP_SESSION_POLICY=channel` — default, current behavior - `BUZZ_ACP_SESSION_POLICY=thread` — new per-thread behavior Being behind a flag means we can enable it for a few agents, watch how it behaves, and roll back instantly without a code change. ## Key design decisions - **Decide the thread once, up front.** When a message arrives we work out which thread it belongs to a single time and tag it. Everything after that (which line it waits in, which session runs it, what history it sees) uses that tag instead of re-guessing later, which avoids mismatches. - **Default stays identical to today.** Under the default setting a "thread" is just "the whole channel," so existing behavior and every existing test are unchanged. The new, riskier behavior is strictly opt-in. - **Give the agent only its thread's history.** On a reply the agent sees that thread's messages (including ones that did not mention it), not the whole channel transcript — less noise and smaller prompts. - **Don't let one channel use more memory than before.** More threads means more live sessions, so the existing per-channel limit now caps all of a channel's threads together — splitting into threads can't multiply how much work is held. ## Bugs found and fixed while iterating (from review) - **Same thread, two sessions.** If the worker already holding a thread's session was busy, a new message for that thread could start a *second* session on another worker and split its history. Now it waits for the right worker instead of forking. - **Interrupting the wrong thread.** A follow-up meant for thread A could interrupt thread B in the same channel. Interrupts now target the exact thread. - **Stuck thread after a crash.** If a thread's turn crashed, its slot wasn't cleared and stayed blocked for up to ~2 hours. It now clears right away and retries. - **Lost the original request.** When a thread was interrupted and then had to wait for a busy worker, only the follow-up was kept and the original request was dropped. The full request is now preserved on retry. - **Same thread seen as two.** Two spellings of the same thread id (upper/lower case) could be treated as different threads. Normalized so they count as one. ## Not in this PR - The desktop Settings toggle and rollout wiring for managed agents — block#6909 - One pre-existing retry edge case (present today without this flag, unrelated to this change) — tracked separately so this PR stays focused. ## Testing The full `buzz-acp` test suite passes (830+ unit and integration tests), plus new focused tests for thread routing, session reuse, interrupt targeting, crash recovery, and request preservation. Behavior with the flag off is unchanged. --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz> Co-authored-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
…lock#6994) PR 2 of the NIP-FI plan: the schema foundation. Establishes the durable server-side identity ledger and final-admission surface that the runtime phases build on. All of Phase A's migrations live here; later phases own their own deltas. Depends on nothing — PR 1 (block#6776, merged) owned zero migration files. This PR's relations are shaped to store exactly what PR 1's verifier produces: issuer-qualified identity and the four denial classes. They meet in a later PR that writes a verified assertion into these tables in one transaction. ## Two internally-ordered migrations - `0041_nip_fi_identity_foundation.sql` (migration A) — core identity + base-lifecycle relations (5 tables): issuer-qualified `(iss, sub)` bindings, lifecycle history/selectors, enrollment policies, and operation receipts. Applies cleanly to current `main`. - `0042_nip_fi_authorization_foundation.sql` (migration B) — the final-admission surface (10 tables): authorization events + capacity, admission results, replay/receipt guards, audit, invalidation domains/floors, protected-object authority, authority epochs, and restore version deltas. Applies to A's resulting state. Fifteen NIP-FI relations total, zero dangling foreign keys. Identity is issuer-qualified throughout — no single-global-issuer assumption in any relation, no `Block`-hardcoding. A single deployment may run one issuer; that is config, not schema. ## Durable, immutable ledger posture All 15 relations are append-only (immutable `no_delete`/`no_truncate` triggers) and carry `community_id` as provenance, not ownership. Both migrations widen the single SQL source of truth `community_write_fence_excluded_table` so the relations are never fence-attached, never purged on community deletion, and never counted as tenant-scoped drift by the deletion control plane's exact-set catalog check — the same posture main already applies to `product_feedback` and `rate_limit_violations`. `schema/schema.sql` keeps one consolidated definition of that function whose exclusion array byte-matches `0042`, guarded by a parity assertion so a future consolidation cannot silently drop NIP-FI relations from the ledger. This makes a tenant's identity/authorization ledger survive community deletion, per the spec's `FI-INV-02` (durable binding) and `FI-INV-03` (tombstone monotonicity) and `NIP-FI.md`'s "durable server state" ruling. `communities(id)` FK never dangles: community rows become permanent tombstones, never hard-deleted. ## Authorization shape and cardinality contracts Authenticated `OperatorDenied` events (`actor_kind` 1–3, non-null `request_fingerprint`) carry a null `semantic_fingerprint` and commit without a denial-attempt row. The denial-attempt cardinality and shape guards are scoped to unresolved pre-auth kind-9 events (`actor_kind = 4`). Applied and no-op lifecycle receipts (`outcome_code IN (1, 3)`) require exactly one mapped success-transition event; denied lifecycle receipts (`outcome_code = 2`) require zero events from the complete core lifecycle success-transition class (kinds 1, 2, 3, 6: enrolled, revoked, rotated, retired) — any such event paired with a denied receipt would record a transition that never occurred. ## Mined vs. new Re-cut from Franco's block#1476 (`0029`/`0030`) and Cea's block#4772 committer schema, re-cut along FK topology and renumbered above the live `main` tip. The buzz-auth core of block#1476 is Cea-authored; `Co-authored-by` reflects verified per-commit authorship of the mined schema. Zero Rust/`deletion.rs` edits — the migration-only exclusion widening keeps `EXPECTED_SCOPED_TABLES` untouched. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Co-authored-by: Cea Stapleton Cordasco <261786559+cea@users.noreply.github.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
…#7135) 🤖 ## Summary - add curated human-readable labels for Databricks Goose models that otherwise render as fully qualified identifiers - render `data_workflow_tools.goose.goose-glm-5-3` as `GLM-5.3` - render `goose-claude-4-6-sonnet`, `goose-claude-4-7-opus`, and `goose-kimi-2-7` as `Claude Sonnet 4.6`, `Claude Opus 4.7`, and `Kimi 2.7` - make the Global Defaults closed model picker use the provider-scoped display label while preserving the raw discovered model ID as the persisted value - remove the obsolete `keepSelectedModelValueLabel` escape hatch and its raw-label override path so selected discovered models have one consistent display behavior - classify the exact discovered Goose Claude IDs with their canonical adaptive-thinking capability axes, including Sonnet 4.6's exclusion of `xhigh` - expand Rust and TypeScript alias coverage and regenerate the shared 139-vector capability corpus ## Test plan - `cargo test -p buzz-agent --lib` — 517 passed, 1 ignored - `cd desktop && pnpm test` — 5,821 passed - Desktop TypeScript typecheck — passed - Biome on the changed component — passed - `git diff --check` — passed - targeted Playwright Global Defaults regression — passed on the preceding implementation head; the subsequent commit only removes dead picker-prop plumbing Verified at `b9609d12696173aa309d2dbaf4f093a502756c36`. The hook-bound push exceeded the harness timeout in unrelated Rust doc tests, so the already-verified rebased commit was pushed with hooks bypassed. Follow-up to block#6955. --------- Signed-off-by: Kalvin Chau <kalvin@block.xyz> Co-authored-by: am <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
🤖 I’m Larry, updating this description on Logan’s behalf. ## Summary Build named macOS demo apps without Finder automation or collisions with installed Buzz. `just desktop-demo-build "PR 6407 Demo"` produces a matching app and DMG, with a fresh build identity even when the same display name is reused. - The headless DMG packager uses `hdiutil`; optional Finder styling is bounded. The existing production release recipe is unchanged. - Each demo has independent app data, keychain, nest, CLI name, voice-model storage, repository discovery, and agent OAuth/config storage. Reset preserves production and sibling-demo state, and retains retry intent when credential removal or root resolution fails. - Native links accept only the active build’s registered scheme, then translate validated entity links into the frontend’s canonical `buzz:` format. - The recipe builds all six executable sidecars. Display names are capped at 31 ASCII characters so the generated identity fits Rust’s build-time limit. **Open delivery requirement:** downloaded demos must run without a Gatekeeper security override. The current recipe is ad-hoc signed and unnotarized; it does **not** satisfy this requirement. Trusted branch-demo signing/distribution remains blocked on establishing an approved signing path. This PR is not being presented as complete download-and-run delivery. ### Related issue N/A — reported in the Buzz DMG-packaging workstream. ### Testing At `11ce21ff97cb387ad676e7caa65b00964097d0bb`, macOS Blox passed the Tauri workspace suite and compiled-flags gate (including the full named-demo state; each library pass: 2,992 passed, 19 ignored), Tauri all-target clippy, the full `buzz-agent` package suite, and frontend lint/typecheck plus 5,733 tests. Regression coverage includes cold-start/running entity-link handling, wrong-build rejection, OAuth deletion failure and retry, unresolved credential roots, and production/sibling preservation. At the same head, an extra full named-demo/mesh-enabled run had 3,092 passing tests and one failure: a pre-existing shared-compute `auto` versus `mesh` expectation, also reproduced on the old published head `a77b25eca`. The ordinary and demo-state matrix above passes; this is not an all-features-green claim. Live macOS Launch Services delivery remains unverified. GitHub CI completed with 30 successful checks and 9 skipped. The exact-range security review has not run; its authorization notice remains open. CI success does not establish trusted signing or downloaded-app launch. Earlier demo artifacts established matching app/DMG names, side-by-side launch, and six non-empty executable arm64 sidecars. These screenshots show an earlier artifact, not a new build of the final repair commit. Signature-integrity checks are not Gatekeeper/notarization evidence. <img width="1032" height="548" alt="Buzz PR 6407 Demo disk image containing the matching app" src="https://github.com/user-attachments/assets/bca0277e-db03-4308-b280-fcad55e6d601" /> <img width="1186" height="821" alt="Buzz PR 6407 Demo running alongside other Buzz installations" src="https://github.com/user-attachments/assets/b4bf4ae5-c341-4e15-8090-9d2ea7c623b6" /> --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Signed-off-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> Co-authored-by: Other Brother Darryl <cee32d92756729ee0c097c5661b879c6199931cd25315c8cf398dcbf0f155cf1@buzz.block.builderlab.xyz> Co-authored-by: Larry <loganj+sandbox-larry@squareup.com> Co-authored-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
## Issue I would send a message in the thread side pane and the main chat's composer would then open its at-mention completion UI: https://github.com/user-attachments/assets/25e79e05-8672-4f6e-b5cc-81a46638f229 ## Summary - Render inline composer autocomplete only for the focused rich-text editor. - Preserve editor focus when opening composer-owned mention controls. - Add an end-to-end regression for sending in a side thread while the main composer retains an agent mention. ## Root cause The main channel composer and thread composer share the channel sending state. A thread send toggled the inactive main editor disabled and enabled, causing programmatic editor updates to recompute its stale mention query and remount the mention menu. ## Verification - Desktop unit suite: 5,556 passed - Persistent agent audience E2E spec: 18 passed - TypeScript typecheck - Vite E2E build - Biome and repository text/pubkey/file-size ratchets --------- Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Jitter <2b2e6415e748c35180847a37aadae06a11e30dddc76b784e7fd9354c4eb42e7a@buzz.block.builderlab.xyz> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Why Thread-scoped ACP sessions from block#6732 need an opt-in desktop rollout that preserves today’s channel policy by default. ## What - Add the default-off “Thread Scoped ACP Sessions” toggle under Settings → Experiments using the existing feature-override persistence. - Map the persisted setting to `BUZZ_ACP_SESSION_POLICY=channel|thread` for every local and provider-backed managed ACP launch. Changes apply when managed agents next start; DMs remain conversation-scoped by the backend. - Keep the desktop setting authoritative over descriptor environment and cover the UI, persistence, local spawn, provider payload, and existing Projects/Workflows experiments. ## Risk Assessment Low. The experiment defaults off and explicitly preserves `channel`; changes are limited to managed-agent launch configuration. Existing running agents are unchanged until their next start. ## Testing - `. ./bin/activate-hermit && just ci` — passed on the final restacked tree, including 5,674 desktop tests, 2,796 Tauri tests, and 1,860 mobile tests. - `cd desktop && pnpm build:e2e && pnpm exec playwright test tests/e2e/experimental-features.spec.ts --project=smoke` — 1 passed. - `. ./bin/activate-hermit && cargo test --manifest-path desktop/src-tauri/Cargo.toml session_policy --lib` — 4 passed. - `. ./bin/activate-hermit && cargo test --manifest-path desktop/src-tauri/Cargo.toml commands::agents::deploy::tests --lib` — 15 passed. - `. ./bin/activate-hermit && cargo test -p buzz-backend-kubernetes --test wire_fixtures` — 4 passed. ## Stack Info Stacked on block#6732. This PR depends on block#6732 and should merge after it. Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz> Co-authored-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
## Summary - add voice-note recording, preview, removal, and send controls to the desktop composer - render sent voice notes as waveform cards with scrubbing and playback-speed controls - transcode recordings locally into the existing relay video-media contract, without relay changes ## Testing - `just ci` - `pnpm exec playwright test tests/e2e/voice-note.spec.ts` - native voice-note media contract test against the relay validator ## Screenshots Focused snapshots are attached below. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz>
## Why PR block#6660 introduced four PostgreSQL-backed persistence tests and a focused CI selector, but an exact test-name list does not automatically cover future database tests. Broad ignored-test execution also exposed shared-schema races and ambiguity between desired-state and migration-applied schema expectations. ## What - Establish a discoverable convention: PostgreSQL unit modules use `postgres_tests`, PostgreSQL integration binaries use a `postgres_` prefix, and unrelated external-infrastructure tests use an `external_infra_` prefix. - Add a dedicated nextest PostgreSQL profile and archive-backed CI job covering the relevant crates without enumerating test names. - Create a run-scoped desired-state source database and a unique PostgreSQL database per test process for parallel-safe isolation, with cleanup on success, failure, or interruption. - Route destructive migration and migration-parity tests to clean `template0` databases while desired-state tests clone the desired-state database. - Document discovery, schema modes, required role privileges, and a bounded portable local runner. - Keep the existing infrastructure-free unit-test jobs unchanged. This is intentionally limited to test and CI harness behavior. It does not move production database code, change persistence semantics, or implement issue block#20's broader shared test-utility refactor. ## Risk Low production risk because all changes are confined to tests, CI configuration, documentation, and test harness scripts. Remaining operational risks: - Cleanup retries `dropdb --force` five times across roughly four seconds. Exhaustion warns with the database name but deliberately does not mask the test result; individual PostgreSQL diagnostics are suppressed. - The seven-package boundary is duplicated between the runner and CI archive and must remain synchronized if PostgreSQL tests move to a new crate. - Portable SHA fallback branches were exercised on Linux; no macOS Blox workstation was needed for this Linux CI artifact. - GitHub's unchanged generic Unit Tests job currently fails while cold-linking `buzz-voice` because `sherpa-onnx-c-api` is absent. The branch changes no voice/build/toolchain inputs; the exact parent previously passed that job, and Blox passes the same infrastructure-free suite (1,450/1,450). A prior-head one-job retry and the final-head run both reproduced the hosted-runner failure. ## Verification Author Blox workstation `2020088`: - `cargo fmt --all -- --check` - `just clippy` - `just test-unit`: 1,450 passed; PostgreSQL tests remain skipped in fast jobs - PR block#6660 focused tests: 4/4 passed through the final runner - Full lane: 282/282 passed in 10.087s with a non-superuser role limited to `CREATEDB`, `CREATEROLE`, and `pg_read_all_stats` - 272 desired-state tests - 10 migration-applied tests - 6 explicitly filtered external-infrastructure tests - Cleanup fault injection: a deliberately underprivileged 282-test run produced the expected 277 passes and 5 failures, and the post-run catalog audit found zero lane databases after retry cleanup. The restored successful run also left zero lane databases. GitHub exact final head `92c231e48f299ea2af23817763f5ae80ac013d68`: - [PostgreSQL Tests](https://github.com/block/buzz/actions/runs/32789030203/job/97630827823): 282/282 passed across 10 binaries in 98.886s; 1,240 skipped, including 6 via the profile filter - The shared relay/PostgreSQL archive predecessor completed successfully - [Unit Tests](https://github.com/block/buzz/actions/runs/32789030203/job/97626930420): unrelated `sherpa-onnx-c-api` native-link failure described above - Independent exact-head review on separate Blox workstation `2022762`: no substantive findings after all initial findings were addressed. The reviewer independently verified cleanup retry behavior, exit-status preservation, shell portability, syntax, and a clean exact-head worktree. ## References - Stacked on block#6660 at exact head `561de54be4d9c2b622b7c2aa5b61bc3068f47e2a` - TheSentinel454#20 - TheSentinel454#20 (comment) Generated with Codex. ## Update — August 24, 2026 review follow-up - Centralized the repeated PostgreSQL test URL resolution in crate-local, test-only helpers without introducing the broader shared utility refactor from issue block#20. - Restored descriptive hybrid/Redis test function names. Structural `external_infra_*_tests` modules now own exclusion, and the nextest filter only recognizes module path segments. - Added a three-second source guard that scans every Rust file and fails CI when an ignored PostgreSQL test would be omitted or an external-infrastructure test would be included. Fixture tests cover accepted modules/binaries and both failure modes. - Removed the two implementation-plan documents. Performance profiling on Blox workstation `2027352` (same prebuilt archive and cargo-nextest 0.9.143 for every comparison): - Current per-test database model, 8 workers: 10.15–10.40s across three runs. The 283 `createdb` calls and 284 `dropdb` calls consumed 28.85–29.46 aggregate seconds, about 39–40% of aggregate test-process duration. - Reusing one database per worker without cleanup: 6.55–6.84s, about 35% faster, but all three runs failed because global matcher-queue tests inherited a quiesced community from an earlier test; the failing test varied with scheduling. - Reusing workers with `TRUNCATE … RESTART IDENTITY CASCADE`: all 282 tests passed, but total time regressed to 10.47–10.67s. The 265 truncate calls consumed 24.34–25.45 aggregate seconds, so truncation merely replaced most clone/drop cost. - Raising concurrency to 16 workers reduced one isolated-database run to 8.79s, but 24 workers exposed a cluster-global `pg_stat_activity` race. The profile remains at the proven-safe 8 workers. Decision: retain unique per-test databases. Harness cleanup uses `dropdb --if-exists --force`; it drops the database rather than truncating tables or deleting rows. Verification at local branch head `60086c1f6ee34a193c342255a5e8e6293b60e988`: - Discovery inventory: exactly 282 intended tests; all 6 hybrid/Redis tests present and structurally excluded. - PR block#6660 focused persistence tests: 4/4 passed. - Full PostgreSQL lane: 282/282 passed three times; 1,240 skipped each run; wall time 9.72–9.96s without profiling shims. - Non-ignored tests with a desired-state database: 1,234/1,234 passed; one mesh-demo timeout from the first run passed immediately in isolation. - `cargo fmt --all -- --check` and all-target/all-feature clippy for `buzz-db`, `buzz-deletion`, and `buzz-relay` passed. Final independent re-review at exact head `42097c0136ab3fa8f0efe5720ae829c04575b9b6` on separate Blox workstation `2028455`: no substantive residual findings. The reviewer independently exercised ordinary strings, zero/one/three-hash raw strings, line/block comments, and string-contained lookalikes, then reran the full 353-file scan, shell syntax, Python AST parsing, and diff check. Independent review found that the source guard's initial regular expression could miss raw-string ignore reasons and treat a commented attribute as real. Final head `42097c0136ab3fa8f0efe5720ae829c04575b9b6` parses valid ordinary/raw Rust string literals only at attributes found in comment-sanitized source; regression fixtures cover both cases. The fixture suite, Python compilation, shell syntax, diff check, and full 353-file repository scan pass on Blox. ## Update — August 25, 2026 restack PR block#6660 merged, so this follow-up was rebased from its former exact parent onto current `origin/main` (`8d2d0ff5ad42733e9949442c4b6358d0ba87f9a8`). The final candidate head is `05dcc2ab28948e3ab79bb44839a69f2ba44648a2`; the PR no longer carries block#6660's pre-squash history. Fresh exact-head verification on Blox workstation `2028572`: - Discovery guard passed across 353 Rust files; inventory remained exactly 282 intended PostgreSQL tests. - PR block#6660 focused persistence tests: 4/4 passed. - Full PostgreSQL lane: 282/282 passed three times; 1,243 skipped each run; wall time 9.70–9.87s. - `cargo fmt --all -- --check` and all-target/all-feature clippy for `buzz-db`, `buzz-deletion`, and `buzz-relay` passed. ## Update — August 25, 2026 final hosted verification - [GitHub PostgreSQL Tests](https://github.com/block/buzz/actions/runs/32801679226/job/97667835080): 282/282 passed in 53.787s; 1,243 skipped. - The final guard scanned all 353 Rust files successfully before the hosted lane ran. - The shared relay/archive prerequisite and infrastructure-free Unit Tests job both passed at exact final head `42097c0136ab3fa8f0efe5720ae829c04575b9b6`. --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> Signed-off-by: tornquist <tornquist@squareup.com>
## Summary - hide the native Download action on voice-note cards - preserve Download for ordinary relay-hosted audio attachments - add focused E2E coverage for both behaviors Follow-up to block#6978. ## Testing - `pnpm -C desktop exec biome check src/features/messages/ui/AudioMessageAttachment.tsx tests/e2e/voice-note.spec.ts` - `pnpm -C desktop typecheck` - `pnpm -C desktop build:e2e` - `pnpm -C desktop exec playwright test tests/e2e/voice-note.spec.ts --project=smoke` (13/13) - pre-push desktop suite (5,881/5,881) Signed-off-by: kenny lopez <klopez4212@gmail.com>
…lock#7109) Depends on block#6994 (merged). Stack: PR 2 block#6994 (merged) → this PR (block#7109) → PR 4 block#7148 → PR 5 ## What Production assertion runtime for NIP-FI Phase A: JWKS caching layer, SSRF-hardened HTTP fetcher, startup validation gate, NIP-11 discovery serialization, federated assertion verifier with sealed key-source authority, and supporting invariant tests. ## Changes ### `crates/buzz-auth/src/nip_fi/jwks/` `ProductionJwksSource<F>` implements the sealed `IssuerKeySource` trait: - `HttpJwksFetcher`: reqwest-backed fetch with SSRF protection — URI validation (HTTPS, no credentials, no fragment, no host matched by the shared enumerated deny policy), per-fetch DNS resolution rejecting any resolved address matched by the shared enumerated deny policy, address pinning to prevent DNS rebinding TOCTOU, redirect denial, incremental body streaming capped at 512 KiB before any parse - IPv6 host extraction via typed `Url::host()` accessor (strips brackets before SSRF check and provides the correct bare input form for reqwest `resolve()` pinning; the bracketed form from `host_str()` fails `IpAddr::parse` and does not match the URL authority key) - Complete-operation deadline via `tokio::time::timeout` covering DNS resolution through body streaming - Bounded periodic refresh with configurable interval and hard snapshot deadline - Cancellation-safe RAII refresh permit: dropped on future cancellation so the next caller can re-fetch - Content-digest-gated generation counter: identical re-fetches preserve generation; key rotations advance it - Injectable clock (`now_fn: Arc<dyn Fn() -> DateTime<Utc> + Send + Sync>`): production uses `Arc::new(Utc::now)`; all four deadline creation and expiry checks use `(self.now_fn)()`, enabling controlled-time testing without wall-clock sleep `JwksSourceContract`: a closed value type that is the single source of truth for the three deployment fields whose change alters which keys the runtime trusts and how long it trusts them: - `jwks_uri` — selects the authenticated key source; validated at construction (HTTPS, no credentials/fragment, no bare private-IP host); stored as the `Url`-normalized form so that equivalent spellings (uppercase host, explicit default port `:443`, dot-segment paths like `/.well-known/./jwks.json`) converge to the same `AssertionPolicyId` - `refresh_interval_seconds` — defines bounded refresh behavior; positive, ≤ 1 year, strictly < `key_snapshot_hard_deadline_seconds` - `key_snapshot_hard_deadline_seconds` — defines the source's accepted time rule; every `VerifiedAssertion.revalidation_dependencies` deadline derives from this `JwksSourceContract` is a required `IssuerPolicy` input and is included in `derive_assertion_policy_id` after a domain separator. `IssuerJwksConfig` embeds the contract instead of independently restating these fields — startup validation rejects any contract mismatch (`NipFiStartupError::JwksContractMismatch`). ### `crates/buzz-auth/src/nip_fi/verifier.rs` - `FederatedAssertionVerifier<S>`: provider-neutral verifier over a closed multi-issuer registry and sealed `IssuerKeySource` - `Arc<S>: IssuerKeySource` forwarding impl (blanket seal for `Arc<S>` in the sealed module) — one `Arc<ProductionJwksSource>` can be shared across multiple verifiers; all observe JWKS refreshes through the shared cache without rebuilding the verifier - `AssertionKeySet`: crate-private constructor seals issuer binding — no external crate can relabel issuer B's JWKS as issuer A - Sealed `IssuerKeySource` trait closes the authority-construction seam at both ends ### `crates/buzz-core/src/network.rs` Renamed `is_private_ip` to `is_not_global_unicast` (compat alias retained) and restored the complete IANA deny/exception table from this branch's own history (`272dacadb`). The predicate is an enumerated deny/explicit exception policy: addresses covered by a named deny rule are rejected; addresses not covered by any explicit deny rule (e.g. `fe00::1`) pass through. Deny rules are derived from the IANA Special-Purpose Address Space registries (last updated 2025-10-09), with globally-reachable exceptions carved out explicitly (e.g. PCP/TURN anycast inside 2001::/23). Blocked IPv4 classes: loopback (127/8), private RFC 1918 (10/8, 172.16/12, 192.168/16), link-local (169.254/16), unspecified (0/8), broadcast, CGNAT/RFC 6598 (100.64/10), benchmarking/RFC 2544 (198.18/15), IETF Protocol Assignments (192.0.0.0/24, globally reachable exceptions: 192.0.0.9 PCP anycast RFC 7723 and 192.0.0.10 TURN anycast RFC 8155), documentation/RFC 5737 (192.0.2/24, 198.51.100/24, 203.0.113/24), deprecated 6to4 relay anycast (192.88.99.0/24, RFC 7526, global=None → conservative deny), multicast/RFC 5771 (224/4), reserved class-E (240/4). Blocked IPv6 classes: loopback (::1), unspecified (::), ULA (fc00::/7), link-local (fe80::/10), deprecated site-local (fec0::/10, RFC 3879), multicast (ff00::/8), IETF Protocol Assignments envelope (2001::/23, globally reachable exceptions: 2001:1::1–::3 PCP/TURN/DNS-SD anycast, 2001:3::/32 AMT RFC 7450, 2001:4:112::/48 AS112-v6 RFC 7535, 2001:20::/28 ORCHIDv2 RFC 7343, 2001:30::/28 DETs RFC 9374), documentation (2001:db8::/32 RFC 3849, 3fff::/20 RFC 9637), 6to4 (2002::/16, RFC 3056), Discard-Only (100::/64, RFC 6666), Dummy IPv6 Prefix (100:0:0:1::/64, RFC 9780), SRv6 SIDs (5f00::/16, RFC 9252), NAT64 local-use (64:ff9b:1::/48, RFC 8215). IPv4 embedded in mapped, compatible, NAT64 well-known (64:ff9b::/96), and SIIT IPv4-translated (::ffff:0:0:0/96) forms is checked recursively. All three callers (JWKS boundary, webhook SSRF, link-preview SSRF) inherit the complete predicate through the inline `is_private_ip` compatibility alias. ### Invariant coverage **Canonical URI convergence.** `jwks_contract_uri_canonicalization_convergence_and_divergence` asserts that uppercase host, explicit `:443`, and dot-segment path (`/.well-known/./jwks.json`) each produce the same `AssertionPolicyId` as the canonical form; a genuinely different host or path diverges. Mutation: storing raw input bytes instead of `parsed.to_string()` turns the three convergence assertions red. **Resolved-target and pin-input seam.** `resolved_target_and_pin_key_seam_public_ipv6_and_fec0_rejection` carries a public `2606:4700::1` URI through all three stages of `fetch_jwks_inner`: `extract_url_host_and_port` yields the bare host (no brackets), `resolve_and_check_ssrf` takes the IP-literal fast path and returns the accepted `IpAddr`, and the extracted host string equals the URL authority form (verifying the correct bare input to reqwest's `resolve()` pin call). `fec0::1` traverses the same extraction and SSRF stages and is rejected as `InvalidUri`. Network-free: both addresses are IP literals with no DNS lookup. Mutation: restoring `host_str()` brackets the address, `IpAddr::parse` fails, the SSRF fast path is unreachable, and all three assertions flip red. **Controlled original-deadline rotation.** `shared_arc_source_verifier_rejects_expired_a1_accepts_a2` uses an `AtomicI64`-backed injectable clock to advance past A1's original absolute deadline without wall-clock sleep. A1's deadline is computed at T0 and never mutated. The clock advances to T0 + HARD_DEADLINE_SECS + 1; `get_snapshot` fires a re-fetch and installs A2. One unchanged `FederatedAssertionVerifier` then rejects A1-signed tokens (deadline enforced by the `key_set` read path) and accepts A2-signed tokens, proves A2's generation is strictly greater, and confirms A2's deadline is later than A1's original. Mutation oracle: replace the shared `Arc` with an independently constructed source built from the same configs and sharing the same controlled clock, warmed with a separate A1 fetch before advancement. Post-advancement, `key_set()` on the verifier's independent source filters the expired A1 snapshot (`filter(|c| now < c.hard_deadline)`) and returns no keys — the verifier never re-fetches and never observes A2. A1-reject stays green (the independent cache is also expired, so no A1 keys are served), but A2-accept flips red, because the verifier never observes A2. A2 acceptance is the reliable shared-source oracle. **Complete SSRF classifier boundary.** JWKS-boundary tests cover every newly restored class through `validate_jwks_uri` (URI-validation path): `192.0.0.1` (IETF Protocol Assignments interior), `192.0.0.9`/`.10` (PCP/TURN anycast global exceptions), `192.88.99.1` (deprecated 6to4 anycast), `2001:2::1` (2001::/23 interior), `2001:1::1` (2001::/23 global exception), `100::1` (Discard-Only), `3fff::1` (documentation), and `5f00::1` (SRv6 SIDs). URI validation and resolved-target enforcement share the same `is_not_global_unicast` predicate, so these URI-path tests exercise the complete classifier table. All pass mutation: removing any deny branch makes the rejection assertion red; removing any exception branch makes the acceptance assertion red. The resolved-target enforcement path is covered separately by `resolved_target_and_pin_key_seam_public_ipv6_and_fec0_rejection` for `::1` (loopback), public `2606:4700::1`, and `fec0::1`. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Cea Stapleton Cordasco <261786559+cea@users.noreply.github.com>
…ock#7143) ## Summary Carl, an automated agent, implementing on Wes's request. Fix ordinary Desktop dev checkouts being misclassified as linked worktrees when launched through `just production`, `staging`, or other recipes that source `instance-env.sh` from `desktop/`. From the repository root, Git reports both directories as `.git`. From `desktop/`, it can report an absolute `--git-dir` and `../.git` for `--git-common-dir`. The textual comparison treats them as different and chooses a branch-suffixed app profile (for example `.dev.main`) instead of the canonical `.dev` profile populated by the agent-adoption script. - Request `--path-format=absolute` for both paths. Actual linked worktrees still have distinct Git/common directories and retain their existing isolated app identities. - Add eight isolated fixture cases executing the real environment script with real Git: ordinary root/subdirectory on main and feature branches, linked root/subdirectory, detached linked subdirectory, and symlinked ordinary subdirectory. Paths contain spaces. Only native icon generation is stubbed; no user profile, keychain, app, or relay is accessed. - Run the regression in the existing always-run CI contract job. No agent migration, global-settings copy, keychain changes, profile merging, branch-label redesign, or packaged-release behavior changes. Existing Git `--path-format` support is required (Git 2.31+). ### Related issue Searched open PRs for `instance-env` and worktree titles, and open issues for dev-profile/adoption reports. No matching path-normalization fix found. - block#7142 touches `instance-env.sh` for descriptive labels. This fix is independent; it uses a distinct test filename to avoid colliding with that PR's new `test-instance-env.sh`. - block#6915 proposes a separate release-profile launch recipe; this fix preserves the current dev-profile contract instead. ### Testing At exact head `0a09a2f3716b0378546b66be2302b750ed4330b9`: - `scripts/test-desktop-instance-detection.sh`: **8/8 passed**. - Before the production change, the same regression failed on the ordinary checkout's `desktop/` case: actual `xyz.block.buzz.app.dev.main`, expected `xyz.block.buzz.app.dev`. - `bash -n scripts/instance-env.sh scripts/test-desktop-instance-detection.sh`: passed. - `git diff --check`: passed before commit; pre-commit and commit-msg hooks completed. - Applicable pre-push gates passed: branch-skew, push-head-scope, and file-size policy (6 tests plus all three entrypoints). Rust/Desktop/Mobile package lanes were correctly inapplicable to this shell/CI-only diff. No native app launch or first-boot key import was performed. The validated boundary is the generated launch environment/profile identity, not successful agent startup. Hosted CI has not been checked or represented as green. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz>
…budgets (block#7185) Raise the `buzz-dev-mcp` shell timeout ceiling to 1,200,000 ms (20 minutes) while keeping the omitted-value default at 120,000 ms (2 minutes), and align the two outer timeout layers that bound every MCP tool call. ## What changed **`crates/buzz-dev-mcp`** — shell timeout cap raised to 1,200,000 ms; tool and parameter descriptions updated to match. **`crates/buzz-agent`** — `BUZZ_AGENT_TOOL_TIMEOUT_SECS` default raised from 660 s to 1,260 s (20-min cap + 60 s cleanup headroom). The old 660 s default would have killed the MCP server at 11 min, before a 20-min shell call could finish. README updated. **`crates/buzz-acp`** — `DEFAULT_IDLE_TIMEOUT_SECS` raised from 900 s to 1,500 s and its doc comment updated (1,200 s max shell + 300 s breathing room). The old value was documented as "600 s max shell + 300 s" — stale arithmetic. ## Tests - Existing boundary tests in `buzz-dev-mcp` (default, exact-cap, over-cap, `u64::MAX`) all pass. - `default_idle_timeout_is_1500_seconds` — pinned constant replaces old 900 s assertion. - `default_tool_timeout_is_1260_seconds` — new pinned constant in `buzz-agent`. - `budget_ordering_invariant_shell_cap_plus_headroom_fits_within_idle_timeout` — new const assertion in `buzz-acp` that encodes the full three-layer ordering: shell cap (1,200 s) ≤ agent tool timeout (1,260 s) < ACP idle timeout (1,500 s) < max turn duration. A future edit that inverts any tier fails to compile. --------- Signed-off-by: Alia <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@buzz.block.builderlab.xyz> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Alia <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@buzz.block.builderlab.xyz> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
Changes `CODEX_REASONING_EFFORT` from `max` to `high` in the `security-review` job. `max` increases both normal review latency and exposure to the upstream PTY-shutdown hang ([openai/codex-action#169](openai/codex-action#169)) where Codex finishes writing its output file but holds stdio open. The composite `uses:` action ignores the 30-minute step timeout and continues running until the 40-minute job timeout fires; at that point GitHub cancels the entire job and the `always()` salvage step never gets to run. `high` retains a high reasoning setting while trading some depth for speed, reducing typical completion time and shrinking the window during which a hung process blocks the salvage path. No other behavior changes. Action pin, CLI version, model, output schema, prompt, and timeout values are unchanged. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
…block#7187) ### What changed? Clear inherited linker flags on the NotificationService target in Debug, Release, and Profile. The extension continues to link BuzzPushKit through its target framework phase, while Runner retains its CocoaPods plugin flags. Extend the macOS mobile CI lane to install Flutter dependencies and build the complete unsigned iOS Release app. This exercises the production Flutter, CocoaPods, Runner, BuzzPushKit, and NotificationService build graph on every mobile change. ### Why? The iOS 0.16 RC archive exposed that NotificationService inherits Runner-only CocoaPods linker flags through the shared Flutter xcconfig. That made the extension link Flutter plugins without the Flutter engine and fail on unresolved Flutter symbols. The previous CI coverage built only the standalone BuzzPushKit package, so it could not detect an app or extension linker regression. ### How is it tested? - A complete unsigned Release build of the Runner scheme succeeded with Xcode and linked, embedded, and validated `NotificationService.appex`. - The new GitHub Actions lane builds the full unsigned iOS Release app with `flutter build ios --release --no-codesign --no-pub`. - Pre-commit and pre-push hooks passed. --------- Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Codex <noreply@openai.com>
## Summary - raise Desktop Rust's differential file-size ceiling from 1,000 to 1,500 lines - raise Desktop frontend and Mobile ceilings from 1,000 to 1,200 lines - keep Web at 1,000 lines and preserve the existing no-growth ratchet above each ceiling ## Why The flat 1,000-line limit is forcing mechanical trimming in Desktop and Mobile even for small cohesive changes. Surface-specific ceilings relieve that pressure without granting Web or every authored component a blanket 2,000-line budget. ## Testing - `just file-size-check` - `cd desktop && pnpm exec biome check scripts/check-file-sizes.mjs` - `node --check mobile/scripts/check-file-sizes.mjs` - `git diff HEAD^ --check` Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz>
A few things to prepare for deploying the Buzz push gateway as implemented in block#6269: - Cut `buzz-push-gateway` chart `0.2.0` from the current config (certificate auth, dogfood client) - Datadog-compatible metrics - Align the desired schema, metrics docs, and production ingress guidance with the current dogfood deployment contract ## Links - [BUZZ-17: Publish the MVP-compatible gateway chart](https://linear.app/squareup/issue/BUZZ-17/publish-the-mvp-compatible-gateway-chart) - [BUZZ-26: Add Datadog-compatible push-gateway metrics and correct source drift](https://linear.app/squareup/issue/BUZZ-26/add-datadog-compatible-push-gateway-metrics-and-correct-source-drift) --------- Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Codex <noreply@openai.com>
Pinky is opening this PR on Wes's behalf. ## Summary Carry forward the fix from block#7103 by **Taksh / @Chessing234**, with the original author and DCO sign-off preserved in commit `99dd19191c`. A separate Pinky-authored commit fixes and strengthens the regression tests. `recover_from_keyring` previously deleted an unreadable keyring identity before checking for a valid leftover `identity.key`. A migrated installation normally has a marker but no file, so that deletion could destroy the only remaining key material. - Attempt valid-file recovery before any explicit keyring deletion. - If a migration marker exists but no valid file can be recovered, enter `Lost` without deleting the unreadable keyring value. - Retain the existing no-marker clear/generate policy. - Correct the stale deletion expectation in `corrupt_keyring_recovers_valid_file_without_rotating`, retaining its identity and persistence checks. - Add an explicit no-delete assertion to `corrupt_keyring_with_valid_file_recovers_before_delete`. Production recovery behavior matches the contributor's fix; follow-up production-file edits only clarify comments and restore a doc comment. The existing migration durability order remains **store → uncached verification → marker → file removal**. Lost-state signing/startup gates and explicit user re-import remain intact. ### Related issue Fixes block#6218. Linked continuation/replacement of block#7103, not a competing implementation. The original PR remains open for maintainer disposition. Searched existing keyring PRs and identity/keyring issues; block#7103 is the direct duplicate and source contribution. ### Testing Validated head: `b6a5512ba85f9e76a039f69714ed04a98eabc710`, based on `4365883151698cd30e31cf4091629543b61c2478`. - **Focused baseline:** `cd desktop/src-tauri && cargo test --lib app_state::` — 52 passed, 0 failed. - **Mutation check:** in an isolated copy, restore the original delete-before-file-probe ordering without changing the test file. `corrupt_keyring_with_valid_file_recovers_before_delete` fails at the new `store.deleted.borrow().is_empty()` assertion (0 passed, 1 failed; expected exit 101). The candidate working tree was never mutated. - **Actual pre-push hooks passed, without bypass:** `push-head-scope`, `branch-skew`, `file-size-check`, and `desktop-tauri-checks`. - `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --workspace --all-targets -- -D warnings` - `cd desktop/src-tauri && cargo test --workspace` — 3,156 passed, 0 failed, 20 ignored across test binaries. - Pre-commit Tauri formatting and DCO hooks passed. - Independent read-only review by Brain found no further defects in the final range and verified the baseline/mutation artifacts and contributor attribution. This is not a GitHub approval. Local validation used the repository's sidecar stubs for compilation. Full-repository `just ci`, a native-app recovery workflow, and live-keyring fault injection were not run. GitHub CI and an actual current-range Codex security review are still pending; local hooks do not substitute for those results. No UI markup changed, so screenshots are not applicable. ### Limits This prevents destructive cleanup on the marker-backed recovery path; it does not identify what originally made the keyring value unreadable or guarantee recovery of already-corrupt material. Markerless last-copy preservation remains a pre-existing limitation outside this change. Only the two app-state Rust files change; no new dependency, workflow, or secret-export surface is introduced. --------- Signed-off-by: Taksh <takshkothari09@gmail.com> Signed-off-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz> Co-authored-by: Taksh <takshkothari09@gmail.com> Co-authored-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz>
…lock#5545) ## What Consolidates all Databricks OAuth acquisition behind one coordinator on `PkceOAuthTokenSource`. Every entry point — the four `TokenSource` methods (`bearer`, `bearer_no_browser`, `refresh_now`, `interactive_login`) plus the public `acquire_with_intent` — routes through a single `acquire()`/`acquire_locked()` core that owns browser and cooldown policy. Before this, acquisition logic was scattered across those methods with no coordination: concurrent callers (Desktop discovery, the saved-agent model picker, managed-runtime inference) could each pop their own browser, and a just-denied attempt would immediately re-prompt on the next passive read. ## How - **Intent policy.** `AuthIntent::{Auto, UserInitiated, Headless}` decides whether a caller may open a browser and whether it honors the cooldown. `Headless` never browses; `Auto` browses but honors an unexpired cooldown; `UserInitiated` browses and bypasses+clears the cooldown. - **Two-layer single-flight per cache key.** An in-process registry (`INFLIGHT`) coalesces same-key, same-intent callers onto one leader's attempt before the file lock. The slot key is `(lock_path, AuthIntent)`, so a `UserInitiated` sign-in never inherits an `Auto` leader's result. A joined result is revalidated against the waiter's own contract. Across processes, callers serialize on a `flock`-based advisory lock and share success through the on-disk cache. RAII `Drop` releases both lock and leader slot. - **Joiner credential-state reconciliation.** `SlotPublish` carries the full `CachedToken` on success. Each joiner reconciles its own independent `state` cell under `state.lock().await` before returning: adopt when absent, expired, or matching the joiner's rejected credential; preserve any distinct newer usable credential. On a matching shared failure, neutralize the joiner's in-memory rejected entry under lock via `expire_rejected_memory` — durable disk mutation is reserved for `acquire_locked` under the cross-process file lock. Without this, a joining source's state remains stale or empty and subsequent plain `bearer()` calls resurface the rejected or absent credential. - **Validate-before-persist boundary.** `finish()` is the candidate-token persistence boundary for refresh and browser results. Before a token is written to cache or the cooldown is cleared, a bearer equal to the caller's rejected bytes yields a typed failure. - **Token neutralization.** When `acquire_locked` enters with `rejected = Some(bytes)`, it calls `expire_rejected()` under the state lock before any cache check. - **Cross-process failure single-flight.** An `AttemptRecord` sidecar records a monotonically-increasing generation, intent, result code, and SHA-256 digest of the completing caller's rejected token. Adoption is temporal (pre-queue snapshot predates current generation) and digest-matched. - **Typed outcomes.** `AuthError` with stable `code()`/`from_code()` replaces display-text matching. - **Durable cooldown sidecar.** Every failed browser attempt is recorded next to the cache key. 5-minute expiry. - **Windows disk persistence disabled.** On non-Unix platforms, `persist()` is a no-op. Lock, cooldown, and attempt sidecars are active on all platforms. Tests that seed or assert on the on-disk token cache are `#[cfg(unix)]`-gated. - **Injected browser opener** invoked while the localhost callback listener is live. ## Tests - `crates/buzz-agent/tests/databricks_auth_coordinator.rs`: browser/cooldown/classification acceptance matrix with a scripted `BrowserOpener` and stub OIDC provider. P1 regressions exercise the full `finish()` → `acquire_locked()` → `acquire_leader()` → `LeaderGuard::complete()` → joiner wiring: - `test_inprocess_joiner_reconciles_stale_state_after_shared_success` (Unix): two real sources both loaded locally-fresh-but-rejected X; after shared success Y, subsequent plain `bearer()` on both returns Y, not X. - `test_inprocess_joiner_neutralizes_rejected_on_matching_shared_failure` (Unix): B's matching rejected X is force-expired in memory after shared `RefreshRejected`; subsequent read cannot return X. - `test_inprocess_joiner_populates_empty_state_no_second_acquisition` (non-Unix): empty A/B join a browser success; B's subsequent headless read returns Y without a second browser (no disk fallback on non-Unix exposes the regression). - `test_crossprocess_userinitiated_waiter_adopts_predecessor_denial` (snapshot-marker barrier replacing an earlier sleep for deterministic generation ordering). - `auth.rs` in-crate tests: lock-primitive edges, disk-recheck on shared failure (`#[cfg(unix)]`), and: - `test_joiner_reconciliation_blocked_until_state_lock_released`: deterministic direct-poll proof that awaited reconciliation requires `state.lock().await`. The test task holds B's state mutex and manually polls a pinned real `acquire()` future — Poll 2 (slot published, mutex still held) must return `Pending` because `lock().await` blocks; with `try_lock` instead, Poll 2 returns `Ready`, failing the assertion. - `test_joiner_preserve_distinct_newer_credential`: deterministic direct polling parks B at `slot.wait()`, then writes Z directly into B's state in the same task, then publishes Y and awaits completion. B must return Y but leave state == Z. Mutation check: unconditional adoption overwrites Z with Y, failing the state assertion. - `test_joiner_shared_failure_recovers_disk_replacement` (Unix): the matching-failure joiner enters the recovery branch — after `expire_rejected_memory` (in-memory, empty state no-op) it reads a sibling-written disk replacement via `usable_from_disk` and returns it. Mutation check: removing the `usable_from_disk` recovery branch returns `Err(RefreshRejected)`. - `test_joiner_failure_does_not_write_disk` (Unix): byte-for-byte disk-invariance regression — a matching-failure joiner calls `expire_rejected_memory` and must not touch the on-disk cache. An independent process C may write a valid replacement between A's failure and B's reconciliation; this guard ensures B's unfenced in-memory neutralization cannot overwrite C's concurrent disk write. Mutation check: reverting to `expire_rejected` rewrites the file (`expires_at = 0`), changing the bytes and failing the assertion. - `test_lock_timeout_leaves_cooldown_sidecar_byte_for_byte_untouched`: a waiter past its deadline returns `LockTimeout` before entering `acquire_locked`; the cooldown sidecar bytes are unchanged. Documents a pre-lock limitation: `LockTimeout` callers do not neutralize state or sidecars. ## Scope / follow-ups - **Runtime 401 handling is deferred.** This PR owns acquisition single-flight and policy. - **Desktop wiring is Phase 2** (not in this PR's boundary). Confined to `crates/buzz-agent/`. - **Windows DACL** is a follow-up once the `windows-sys` binding is available. ## Stack Built on [block#5534](block#5534) (`hayt/databricks-oauth-cache-hardening`), now merged. Retargeted to `main`. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary - add the canonical public Databricks `Claude Fable 5.1` model record - match the existing Fable family contract: adaptive thinking, `low|medium|high|xhigh|max`, default `high`, Anthropic Messages, and no normalization - add generated Rust/TypeScript corpus coverage and canonical Global Defaults label/persistence coverage - remove deployment-specific catalog references from the repository ## Scope This is metadata and regression coverage only. It does not change model-capability resolution or production frontend logic. ## Validation - `cargo fmt --check` - `node --test desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs` - `cargo test -p buzz-agent` - `pnpm build:e2e` - `pnpm exec playwright test --project=smoke --grep 'defaults render the Fable 5.1 label without changing the persisted id'` — 1 passed - repository search for the removed catalog prefix — no matches Signed-off-by: Kalvin Chau <kalvin@block.xyz> Co-authored-by: am <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
**Category:** fix **User Impact:** Top-level channel messages now notify only agents explicitly selected for that message, while thread replies visibly retain their addressed agents. **Problem:** Channel-root and thread composers presented the same automatic-mention model even though retained recipients are only predictable within an ongoing thread. That could make a new top-level message notify an agent the sender did not deliberately choose for that message. **Solution:** Make retained audiences a thread-only capability. Root messages remain explicit and one-shot; threads retain visible, removable agent recipients, with the automatic-mention setting exposed directly in the mention picker. <details> <summary>File changes</summary> **desktop/src/features/channels/ui/ChannelPane.tsx** Removes persistent audience state from the channel-root composer. **desktop/src/features/messages/ui/ComposerAddressControls.tsx** Uses the broader **Manage mentions** label because the picker includes people as well as automatic agent controls. **desktop/src/features/messages/ui/ComposerAddressControls.test.mjs** Locks the updated accessible label and active treatment. **desktop/src/features/messages/ui/MentionAutocomplete.tsx** Shows the right-aligned automatic-mention setting directly, uses thread-specific copy, preserves keyboard/focus behavior, and keeps the current mention when retention is unchecked. **desktop/src/features/messages/ui/MentionAutocomplete.test.mjs** Covers the always-visible setting, compact layout, copy, and thread-scoped agent actions. **desktop/src/features/messages/ui/MessageComposer.tsx** Separates unpinning an agent for future replies from removing its current draft mention. **desktop/src/features/messages/ui/MessageComposer.types.ts** Narrows retained audience contexts to threads. **desktop/src/features/messages/ui/persistentAgentAudienceHosts.test.mjs** Prevents channel-root and new-message hosts from opting back into retained audiences. **desktop/src/features/messages/ui/useAgentAddressLockPicker.ts** Splits unpin and current-mention removal semantics. **desktop/src/features/messages/ui/useAgentAddressLockPicker.test.mjs** Verifies unpinning retains the current draft mention. **desktop/src/features/settings/ui/AgentsSettingsPanel.tsx** Describes the preference as addressing selected agents in thread replies. **desktop/tests/e2e/persistent-agent-audience.spec.ts** Moves retained-audience lifecycle coverage to thread composers and adds root, settings, layout, focus, keyboard, unpin, and draft regressions. **desktop/src/features/messages/ui/MessageComposerAutocompletes.tsx** Preserves composer focus ownership while routing the thread-only controls. **desktop/src/features/messages/ui/useComposerFocusOwnership.ts** Keeps focus within the composer while interacting with its mention overlay controls. </details> ### Reproduction steps 1. Enable **Automatically mention agents** under agent settings. 2. In a channel root, select an agent and send a message. Confirm the agent is addressed once, no retained-recipient control appears, and the next root message has no agent recipient. 3. Open a thread and select an agent. Confirm the visible recipient persists into later replies. 4. Open **Manage mentions** in the thread composer. Confirm the automatic-mention setting is immediately visible, right-aligned, and labeled **Address selected agents in thread replies**. 5. Uncheck a selected agent. Confirm its current draft mention remains, while later replies no longer retain it automatically. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
## Why The monolithic CI workflow is a frequent merge-conflict hotspot. Splitting cohesive domains into same-repository reusable workflows keeps one centrally filtered entry point while letting Rust, desktop, relay/PostgreSQL, client, and security CI evolve independently. ## What - Keep `ci.yml` as the only push/pull-request orchestrator with unchanged concurrency and path detection. - Move 18 execution jobs into five `workflow_call`-only domain workflows without changing their runners, steps, matrices, caches, artifacts, permissions, or timeouts. - Keep the relay artifact producer with desktop integration, the complete PostgreSQL lane, and relay E2E consumers. - Preserve all 12 existing required GitHub Actions contexts through lightweight top-level compatibility gates, so the repository ruleset does not need to change. - Update the Rust-cache contract to follow Unit Tests into `_ci-rust.yml`. ## Risk Assessment CI-only change with moderate workflow-orchestration risk. The main risks are reusable-workflow output propagation, skip behavior, and visible check naming; the old required names remain explicit top-level jobs, and the draft will stay open until an exact-head GitHub Actions run and independent review are complete. Generated with Codex --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> Signed-off-by: tornquist <tornquist@squareup.com>
…k#7250) Replace the real user name in the shared ACP mention guidance with the fictional `Alice Smith` example. Preserve the exact-display-name and no-inference instructions while avoiding prompt priming from a real user identity. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Alia <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@buzz.block.builderlab.xyz> Co-authored-by: Alia <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@buzz.block.builderlab.xyz>
Removes the dead relay-side authority ledger introduced by migrations 0041 and 0042, and the dead `require_attested_key` verifier knob from `buzz-auth`. Both are unreachable by design under NIP-FI spec v2 (block#7214, squash `d4420eb47`), which makes OSS Buzz stateless for identity: the relay neither stores nor verifies an authority chain. ## What changes **`migrations/0044_drop_nip_fi_ledger.sql`** Drops all fifteen NIP-FI ledger tables and their trigger functions using `CASCADE` to resolve the circular deferred FK between `identity_bindings` and `identity_lifecycle_history`. Drops proceed in FK dependency order: selectors → history/bindings → enrollment_policies/receipts → parallel drop of auth tables. Restores `community_write_fence_excluded_table` to its pre-0041 body (removes NIP-FI table names from the exclusion array). **`schema/schema.sql`** Removes the NIP-FI section (~1885 lines of tables, functions, and triggers) and updates `community_write_fence_excluded_table` to match. **`crates/buzz-db/src/runtime/migration.rs`** - Updates the `embedded_migrator_contains_consolidated_initial_schema` sanity check: count 43→44, adds 0044 assertion block (verifies `DROP TABLE` statements and absence of NIP-FI names from `schema.sql`). - Removes ~2580 lines of NIP-FI Postgres integration tests (all `#[tokio::test] #[ignore = "requires Postgres"]` from the 0041/0042 behavioral coverage). - Removes the `extract_excluded_table_array` drift check (0042 body no longer matches `schema.sql` by design). - Adds `migration_0044_drops_populated_nip_fi_ledger_cleanly`: runs migrations to 0042, seeds rows in `authorization_operation_receipts` and `authorization_invalidation_domains`, then runs to 0044 and verifies all fifteen NIP-FI tables are absent. **`crates/buzz-auth/src/nip_fi/config.rs`** Removes `require_attested_key: bool` from `IssuerPolicy` — field, constructor parameter, accessor, and its contribution to `derive_assertion_policy_id`. **`crates/buzz-auth/src/nip_fi/verifier.rs`** `parse_nostr_pubkey_claim` no longer takes a `policy` parameter. The `None` (absent claim) arm now returns `Err(VerifierError::ClaimRejected)` unconditionally instead of conditionally on `policy.require_attested_key()`. **`crates/buzz-auth/src/nip_fi/verifier/tests.rs`** - Removes `missing_nostr_pubkey_denies_under_attested_key_policy` (the sole `require_attested_key: true` call site). - Removes `false,` from all eleven `IssuerPolicy::new` call sites. - Injects `nostr_pubkey` by default in `mint_signed_by` (spec v2 requires it unconditionally). - Updates `valid_access_token_verifies` to assert `asserted_key().is_some()`. **`crates/buzz-auth/src/nip_fi/startup/tests.rs` + `jwks/tests.rs`** Removes `false,` from all `IssuerPolicy::new` call sites and adds `nostr_pubkey` to all token-minting helpers. ## Verification - Fresh-DB migration run to head: all migrations apply cleanly in sequence. - Populated-0041/0042-DB migration through 0044: seeds rows in live NIP-FI tables, verifies all fifteen are dropped without error. Closes the dead-code inventory item from the spec-v2 cleanup plan (channel `48374f48`). Follows block#7214. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
…ock#4625) ## Summary Genericizes the agent effort write-side so Goose participates in the same canonical effort contract as buzz-agent. A spawn bridge translates the canonical key to whatever the target harness expects at launch time. Read/write/spawn paths all derive their vocabulary from runtime metadata rather than a hardcoded buzz-agent list. ## What changed ### Rust — config bridge + spawn path - `apply_spawn_effort_env` in `effort.rs`: production command-boundary seam — writes baked env, runs the effort projection, strips per-runtime suppress set, and emits exactly one projected key. - `apply_effort_to_spawn_command` in `runtime.rs`: thin wrapper returning a `#[must_use] EffortApplied(())` token (private field — unforgeable outside the function). `spawn_agent_child` calls it as `let effort = apply_effort_to_spawn_command(...)` and passes `effort` to `spawn_with_effort_proof`. Deleting the call is a compile error: `effort` is undefined at the `spawn_with_effort_proof` site. Deleting `apply_spawn_effort_env` inside the wrapper turns the production-sequence tests RED. - `apply_record_field_updates` in `agent_models_update.rs`: returns `Result<RecordFieldsApplied, String>` (`#[must_use]` token). `update_managed_agent` calls it as `let applied = apply_record_field_updates(...)?` then passes `applied` to `stamp_record_updated_at`. Deleting the call is a compile error: `applied` is undefined at the `stamp_record_updated_at` site. - Unknown/custom-runtime passthrough: `apply_effort_launch_to_command` skips the suppress loop when `preserve_passthrough && value.is_none()`, preserving ambient ACP sentinels. - `EnvVarGuard`: prior value stored as `OsString` (`var_os`) so non-Unicode values are restored exactly on Drop. A single `PROCESS_ENV_MUTEX` in `managed_agents/mod.rs` is shared by `lock_path_mutex()` and `lock_env_mutex()` — any two tests calling either helper are mutually exclusive with each other. Tests in other modules (`app_state_tests`, `agent_config_tests`, `reader_tests`) maintain their own independent locks and are not in this domain. - Dead-code: `strip_effort_keys_from_command` marked `#[cfg(test)]`; import path in `effort_cmd_tests.rs` fixed. - Windows CI fix: platform-gated variants for inherited-env tests. ### TypeScript — renderer + model cleanup - `AgentConfigFields` orphan-model cleanup effect: the `isHarnessNativeEffort` early-return was skipping the model clear on provider→Custom transitions. Refined to: return early only when model is already null; clear model once while preserving the harness-native effort key (Carl P2). - Provider-empty convergence: when model is null and effort is native, the cleanup effect returns early (nothing to clear) — prevents spurious `onConfigChange` loop. - `EffortSelectField` / `humanizeEffortLabel`: runtime-native option labels title-cased (`off` → `Off`) with raw canonical values preserved for round-trip fidelity. - `AgentConfigFields`: drives effort renderer from `selectedRuntime.effortCanonicalValues` (harness-native path) or the model/provider catalog (buzz-agent/provider path), selected by `isHarnessNativeEffort`. ### Docs - `desktop/src/features/agents/AGENTS.md` item 14: updated from deleted `persistAgentEffortLevel` direct-write contract to the shipped Save-gated `update_managed_agent.effortLevel` path. Consistent with `EffortPickerField`'s own doc comment. ### Tests - `agent_models_update_tests.rs`: seam tests via `apply_record_field_updates` — non-local rejects, local set/clear, ordering invariant, ACP-sentinel sweep. `record_field_updates_persist_effort_to_disk` (renamed from the prior false-claim name) drives load→apply→stamp→save→load via a mock AppHandle + tempdir, asserting `effort_level` persists to disk. Manual HOME/XDG restore replaced with RAII `EnvVarGuard` (panic-safe, `OsString`-exact). - `effort_cmd_tests.rs` / `effort_tests.rs`: production-sequence seam tests via `apply_effort_to_spawn_command`. Spawns `/usr/bin/env` to verify child's real env. `EnvVarGuard` for panic-safe restore. Windows twin using `cmd /c set`. - `effortAutoClear.test.mjs`: five mounted stateful journeys via `AgentConfigFields` with `useCustomSelect=true`. Covers: custom trigger shows "Off" at mount; provider-empty mount is a stable fixed point; provider→Custom switch converges; stale Anthropic model cleared on Custom switch with Goose effort preserved (Carl P2 regression); Settings-style Save/reread preserves effort. - `agentDefaultsEditor.test.mjs`: two full Save/Next journey tests through the real production parent trees. Both start with `GOOSE_THINKING_EFFORT: "low"` and operate the real Popover-based effort control (click trigger → click "off" option) before Save/Next, asserting zero writes after selection. The `set_global_agent_config` stub captures the submitted payload; each test asserts raw `GOOSE_THINKING_EFFORT: "off"` in the captured config. The stub stores its canonical response from the actual payload; the fresh remount's `get_global_agent_config` returns that stored object (not a hand-written fixture), then asserts "Off" shown. The `DefaultConfigStep` test starts with `isDirty: false` — the real-control effort selection calls `onConfigChange → updateDraft → isDirtyRef=true`, making the `commit()` on Next load-bearing. ## Mutation evidence - Delete `let effort = apply_effort_to_spawn_command(...)` call from `spawn_agent_child` → compile error: `error[E0425]: cannot find value `effort`` at `spawn_with_effort_proof` site. - Delete `let applied = apply_record_field_updates(...)?` from `update_managed_agent` → compile error: `error[E0425]: cannot find value `applied`` at `stamp_record_updated_at` site. - Delete `apply_spawn_effort_env` from inside `apply_effort_to_spawn_command` wrapper → `production_sequence_goose_inherited_collision_resolved_in_child` RED. - Revert `isHarnessNativeEffort &&` guard in cleanup `useEffect` to bare `if (isHarnessNativeEffort) return` → stale model not cleared → Carl P2 regression test RED. - Remove `isHarnessNativeEffort ||` from the nothing-to-clear condition → provider-empty mount emits `onConfigChange` → loop test RED. - Remove `isHarnessNativeEffort` branch in `AgentConfigFields.tsx:634-636` → both `agentDefaultsEditor.test.mjs` mount assertions fail: trigger shows "Select" instead of initial effort label. - Remove `preserve_passthrough` guard in `apply_effort_launch_to_command` → `production_sequence_custom_inherited_acp_sentinel_survives` RED. - Drop `GOOSE_THINKING_EFFORT` from the `set_global_agent_config` stub payload → payload assertion in `agentDefaultsEditor.test.mjs` fails (`undefined !== "off"`) → RED (verified). - Remove the effort-select dirtying steps from the `DefaultConfigStep` test (so `isDirty` stays false) → `commit()` is a no-op → write-count assertion after Next fails (0 instead of 1) → RED. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
🤖 ## Summary An agent you own could be missing from **New message → To:** and **Channel members → Add people and agents** on a machine that has never managed it. This PR lets those existing lists find your agent without requiring a shared channel first. Desktop now checks records proving you own it, rather than looking only at agents in channels you've already joined. **No new screen or control is added.** For example, an agent with verified ownership and **Who can send instructions → Only me (default)** can now appear even with no shared channels. Each screen still applies its existing access rules; this does not make every discovered agent selectable everywhere. | Screen / control | Before | After this PR alone | | --- | --- | --- | | **New message → To:** recipient picker | An owned agent absent from this machine and shared-channel bot lists could be missing. | Its named **agent** row can appear; selecting it adds a recipient chip. This is recipient selection, not a guarantee that a later message will reach or wake the agent. | | **Channel members → Add people and agents** | The same agent could be missing from **Not in this channel** search results. | Its row can appear with the existing **Add** button. If you can add members, that button submits the existing channel-membership request; finding the row alone changes no membership. | | **Stream / forum composer → @ suggestions** | An owned agent already in the channel under an ordinary member role could be missing from agent suggestions. | Its actual membership is recognized without requiring the bot role. Agents not managed on this device still need membership in that channel. | | **Pulse → Agents** | An agent absent from both local management and the server's agent list was omitted from the count and author lookup. | The count and feed's author lookup can include it; notes appear only if it has published them. | Being listed does **not** mean the agent is online, add it to a channel, or grant local Start/Edit controls. For agents not managed on this device, global **Search** still excludes those configured for “Only me”, and DM @ selection is not added here. DM @ selection and message-driven nonmember invitation are addressed in [block#7124](block#7124); the standalone forum **Invite / Cancel** flow is in [block#7125](block#7125). <details> <summary>Ownership and membership checks</summary> A discovery lead is not proof: the latest agent profile must have a valid signature and exactly one valid ownership attestation—the owner's signed link to that agent. Its response policy must be signed by that verified owner; an invalid latest policy cannot restore an older permission. Membership comes separately from the latest server-signed roster, including removals. Existing profile cards, owner labels and agent-avatar shapes also use this stricter verification: malformed or forged evidence must not supply ownership/agent classification on its own. Valid ownership was already recognized; no profile-picture or badge design changes. Attestation time conditions apply to the signed event's timestamp, not a live expiry timer. Existing legacy compatibility and builds requiring verified owner policy retain their respective rules. Discovery and sending remain separate operations, not an atomic permission check. </details> ### Review corrections - When runtime and owner policy overlap, **explicit online/away/offline from the verified latest runtime is retained**. Policy still supplies ownership/permissions; claimed runtime membership is not restored. Missing/unrecognized status stays unknown, and invalid latest policy cannot revive runtime permissions. - Discovery without runtime evidence is now **unknown**, not offline: native conversion, both IPC adapters, Pulse, Projects and profile/session consumers preserve that distinction. Unknown has no status dot and is not promoted to a deployed/running agent. - Both relay-only picker paths retain the authenticated owner, including the existing **managed by you** label. The analogous global Search projection is fixed without changing its existing “anyone” filter. - Authorized stored profile activity remains visible when liveness becomes unknown/absent or the active turn ends. History reads do not start a live subscription, grant access, or imply current availability. ### Related issue Independent base: `main`. Child: [block#7124](block#7124), then [block#7125](block#7125). Extracted from [block#7114](block#7114), retained as historical source (`98fe33ec`). [Behavior contract](https://github.com/block/buzz/blob/3a56d17824522580fe04cae463b54f4c7ba66021/docs/owned-agent-discovery.md). Originating [Buzz discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848) · channel `f7a9536a-1738-4bad-a888-b3ea25010ef1`. ### Testing Current candidate: `3a56d17824522580fe04cae463b54f4c7ba66021`, a four-file native/test/doc runtime-status repair atop published `ae23c1c9680a881cee7eed94e259bf15bf8ce3f7`. Branch ancestry is main `1c8321cd08feb597f8bcff5195c21148fb3e98ed`; refreshed main `0e878664b08cdf7fb2d89d940bc2aa92cdc485f7` adds only the independent CI-workflow split. Read-only mergeability succeeds; this is not a tested merged-tree claim. **Local CI attempt and continuation (not an uninterrupted green run):** the new exact-head `just ci` passed formatting/static checks, workspace and Tauri clippy, workspace Rust tests, **5,910 desktop tests**, desktop production build and Tauri check. Its native main target finished **3,073 passed / 1 failed / 19 ignored** (exit 101): `cheap_discovery_reports_absent_before_any_forced_probe` saw a process-global login-shell counter of 2 instead of 0. The counter includes unrelated version/adapter probes whose tests do not hold the failed test's PATH mutex; no managed-agent discovery implementation changed in the runtime repair. The unchanged failing test then passed **three isolated invocations**. Only the failed native workspace lane was retried with `RUST_TEST_THREADS=1 just desktop-tauri-test`: **3,074 main-target tests passed / 19 ignored**, all additional workspace targets passed (exit 0). The previously unrun `just web-build mobile-test` tail then passed (exit 0; **2,019 mobile tests**). Earlier successful lanes were reused; no source/guard changes or blanket CI rerun. The original failure and all diagnostic/retry logs are retained. - **71 native `nostr_convert` tests pass**, including seven new production merge regressions: online/away/offline, missing/invalid status, policy-only, status-less latest replacement and forged latest replacement. Before production repair, those seven yielded **4 failures / 3 passing controls**. - Reused frontend evidence from `ae23c1c9` (frontend is unchanged): Desktop TypeScript and isolated E2E build pass; **9 browser tests / 0 retries**, covering both relay-only picker journeys and seven adjacent stop-control regressions. Real UI with mock Tauri IPC, not live relay/native webview. - Earlier `ae23c1c9` local `just ci` passed without failures, including 3,067 native main-target tests / 19 ignored and 2,019 mobile tests; not substituted for the new source gate above. - Reused unchanged repair evidence: **17 real-store/hook history regressions**, **161 focused tests**, and independent **9 mounted owner/bot/identity revocation/regrant transitions** with zero hook-phase native calls. The regression was falsified before repair (14 failures, 3 controls). - Signed local-server fixtures cover discovery with no local/shared record, ordinary-role membership, forged ownership, invalid signatures, duplicate authentication, wrong-owner/latest-invalid policy, revoked membership and wrong destinations. These establish native data checks, not a live agent response. GitHub checks and renewed technical/security review must apply to the current published head; earlier-head green checks are not replacement-head proof. Local source review is not formal code-owner/latest-push approval or exact-range security authorization. A green security workflow with substantive review skipped is not security clearance. ### Screenshots #### Relay-only picker evidence — `ae23c1c9680a881cee7eed94e259bf15bf8ce3f7` These cropped rows come from the two real production picker journeys in [`owned-agent-discovery.spec.ts`](https://github.com/block/buzz/blob/ae23c1c9680a881cee7eed94e259bf15bf8ce3f7/desktop/tests/e2e/owned-agent-discovery.spec.ts), using mock Tauri IPC with **no local agents and no user-search duplicate**. The fixture supplies verified-owner data and unknown availability; the browser test checks its presentation, not native signature verification. Both exact-tip journeys pass without retries. No live relay, native webview, invitation, delivery or wakeup is claimed. Before the repair, both relay-only candidate constructors discarded the owner, so the existing “managed by you” label was absent. These are after-repair captures; no before image was captured. #### New Message → To The relay-only agent retains its authenticated owner label.  #### Channel members → Add people and agents The matching result retains “managed by you” beside the existing Add action; the test does not click Add or claim membership changed.  --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
…lock#7131) 🤖 ## Summary In Buzz Desktop, clicking a message from stopped agent A could open running agent B—and B's controls—because both shared a persona (an agent definition). This now opens the author you clicked and only that agent's own controls, so you can inspect an old message without being redirected to a different running agent. An explicit public key—the identifier for one agent—now stays exact across message authors, members, DMs, deep links and Instances rows, including stopped, archived and relay-only agents. Local controls come only from a matching local record for that key. A relay-only A cannot borrow B's Start/Stop/Edit controls or configuration. Deliberately opening a **persona** is different: it can still select a representative that respects archived instances or offer Start when none remains. The change removes competing historical-persona redirects rather than adding another identity exception. ### Related issue Independent base: `main`; no stack parent or child among the replacements. Extracted from [block#7114](block#7114), retained as historical source (`98fe33ec`). [Behavior contract](https://github.com/block/buzz/blob/9c4b6523ceaef0f3d92906fcdb5d9a3b9ede7e17/docs/agent-profile-identity.md). Originating [Buzz discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848) · channel `f7a9536a-1738-4bad-a888-b3ea25010ef1`. ### Testing Synthetic Playwright mock-bridge state. After screenshots exercise this independent profile extraction (`df6612b1`); no availability or cloud-marker implementation is included. #### Before: historical A redirects to running B Unchanged main product code (`bc006f67`) with the same updated historical-message fixture fails: clicking Earlier Parity Agent opens Current Parity Agent and its Stop control.  #### After: historical A opens A The clicked author remains Earlier Parity Agent, with A's public key and its own Start control. The current sibling is not substituted.  #### Exact relay-only A while local sibling B exists A's public key and owner-scoped profile are visible; no local Start/Stop/Edit/Add control or sibling definition is borrowed.  #### Explicit persona navigation may select local B Deliberately opening the persona selects its local representative, with B's key and legitimate Stop/Restart/Edit controls.  #### Explicit persona without an instance may offer Start This is a deliberately opened persona, not a relay-only key turned into a persona surface.  [Original screenshot publication](block#7131 (comment)); all five immutable image URLs and captions retained here. The final documentation-only commit does not change this UI. These are synthetic browser fixtures, not live runtime health evidence. To check manually, open an old message from stopped A while same-persona B is running; compare the displayed key and controls. Then open the persona itself and verify that representative selection still works. #### Evidence and limitations **5,793 desktop tests**, **56 profile/archive browser cases**, type/static/size checks and repository-wide `just ci` passed. The historical-message regression fails on unchanged main by opening B instead of A. [Published-head CI passed](https://github.com/block/buzz/actions/runs/33422207592). The [advisory security check](https://github.com/block/buzz/actions/runs/33422240973) timed out without a result; it is not a passing check. No availability, cloud-marker, discovery or mention-routing change is included. These screenshots do not establish remote delivery, agent execution or termination. #### Security authorization history (audit, not clearance) The [security gate](block#7131 (comment)) remains visible and unresolved. Existing authorization-request comments were posted by `loganj`: [old-head request](block#7131 (comment)) for `df6612b1db5a6f8d128cef955fd66a80b6828cb8` at 2026-08-31 17:55:11 UTC, then [current-head request](block#7131 (comment)) for `9c4b6523ceaef0f3d92906fcdb5d9a3b9ede7e17` at 17:55:57 UTC. The existing [issue-comment workflow run](https://github.com/block/buzz/actions/runs/33422240973) ended cancelled after the previously reported timeout; it did not produce a completed security review. Latest exact-head Run/Post Codex jobs are skipped, not security approval. Historical comments remain available at their original links; consolidating their audit here does not withdraw authorization or clear the gate. An authorized security workflow owner must arrange the missing exact-range result. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
## Why
Buzz already reports coarse writer/reader database checkout waits, but
those
signals cannot explain which startup or serving operation is blocked by
pool
pressure. That makes rollout diagnosis and postmortems ambiguous: a
readiness
probe, NIP-42 authentication, authorization check, reconnect history
repair,
event write, and background maintenance can all wait on the same pool
while
appearing identical.
This PR implements Package 2A of the pod-handoff plan: operation-aware
pool
borrow causality. It is observability-only; it does not change
configured pool
sizes, SQL semantics, transaction ordering, or timeout policy. Physical
DNS/TCP/TLS/Postgres authentication and session initialization remain
the
separate Package 2B boundary.
## Metric contract
The final contract separates three questions:
| Question | Metric |
|---|---|
| How long did checkout wait? |
`buzz_db_pool_acquire_duration_seconds{pool_role,operation}` |
| How did the attempt end? |
`buzz_db_pool_acquire_attempts_total{pool_role,operation,outcome}` |
| Who is waiting now for a tracked operation? |
`buzz_db_pool_waiters{pool_role,operation}` |
Outcomes are `success`, `timeout`, `error`, and `cancelled`. Operations
are
`bootstrap`, `readiness`, `tenant_resolution`, `authentication`,
`authorization`, `subscription_history`, `event_write`, and
`maintenance`.
Only these eleven pool/operation pairs are constructible:
```text
writer/bootstrap reader/bootstrap
writer/readiness
writer/tenant_resolution
writer/authentication
writer/authorization reader/authorization
writer/subscription_history reader/subscription_history
writer/event_write
writer/maintenance
```
The duration histogram intentionally does not carry `outcome`. Result
data
remains available on the terminal counter for historical counts and
rates,
without multiplying the expensive histogram family. Nine finite buckets
plus
`+Inf`, sum, and count produce 12 duration series per valid pair.
Together with
four outcome counters and one waiter gauge, the new-family ceiling is
exactly
187 raw Prometheus series per pod, asserted from the production
exporter.
The existing coarse acquisition families remain temporarily for
dashboard
compatibility while the new series are validated in staging.
The operation-specific waiter family covers the explicitly routed,
deployment-critical operations above; it is not a census of every
possible
SQLx checkout in the process. The dashboard pairs it with SQLx pool
active/idle/max gauges for whole-pool capacity context, and treats a
missing
operation series as unknown rather than healthy zero.
## What changed
### Cancellation-safe acquisition ownership
- Add writer- and reader-specific typed operation APIs so invalid label
pairs
cannot be constructed and store modules cannot emit reader labels.
- Own every polled acquisition with one RAII terminal guard.
- Record exactly one duration and terminal outcome for success, timeout,
error,
or cancellation.
- Emit nothing for a future that is created but never polled.
- Balance the operation-specific waiter count exactly once on every
terminal or
dropped future.
- Periodically refresh every expected waiter pair, including healthy
zero, so
missing telemetry is not presented as zero. Reader pairs are emitted
only
when a distinct read pool is configured; a writer-only pod cannot
fabricate
healthy reader-zero state.
### Production attribution
Route the deployment-critical acquisition paths through caller-owned
semantic
entry points, including:
- writer and reader bootstrap;
- the real post-block#7149 readiness acquisition and deletion-catalog
validation;
- tenant resolution and community lifecycle checks;
- NIP-42 allowlist authentication;
- membership, moderation, invite, operator, Git, agent-owner, and policy
authorization;
- operator community create/list/archive/unarchive, reverse host/channel
tenant
resolution, and REQ row-community conformance lookups;
- writer/reader subscription history, feed, thread, and routed fallback
paths;
- primary and command event writes, replaceable events, mention
indexing,
reaction/channel/member/archive side effects, and thread metadata;
- push matching, usage rollups/leadership, replica-fence startup and
recurring
probes, periodic reconciliation, channel/deletion reapers, partitions,
and
other bounded maintenance/bootstrap paths.
Shared helpers now accept caller-owned intent or expose named semantic
variants
instead of assigning one misleading operation to every caller. No known
P0 path
uses `other`.
### Readiness and size-one-pool correctness
- Rebase on the post-block#7149 readiness implementation and instrument the
actual
`Db::readiness_check` acquisition rather than the superseded ping-only
seam.
- Acquire once for deletion-catalog validation and run its queries on
that
connection, preserving the shared readiness deadline.
- Scope the channel-roster catalog checkout before the behavior probe so
a
size-one writer pool cannot self-deadlock during startup verification.
### Exporter, documentation, and CI
- Register metric HELP/type/unit metadata through the production
Prometheus
builder.
- Configure dedicated checkout buckets at 1ms, 5ms, 10ms, 25ms, 50ms,
150ms,
500ms, 1s, and 3s.
- Add a production scrape-contract test for exact names, labels,
buckets,
valid pairs, sensitive-label exclusion, and the 187-series ceiling.
- Add source mutation guards for the P0 semantic entry points and
raw-checkout
bypasses.
- Add an exact backend-integration CI selector for the production
attribution,
cancellation, readiness, and size-one-pool PostgreSQL tests.
- Document the frozen label vocabulary, valid combinations, semantics,
and
cardinality budget in the Helm chart README.
## Dashboard intent
The new Stage 2 row in **Buzz Startup & Rollout Safety** is
deployment-first:
- baseline-versus-candidate attempts, failure rates, cancellation rates,
and
maximum wait by operation;
- outcome counts and percentages over time by SHA/ReplicaSet;
- acquisition wait heatmap, average, and maximum through the rollout;
- historical waiter pressure beside writer active/idle/max context;
- per-pod postmortem drilldown, including terminated pods;
- a smaller current-waiter table with explicit stale/missing semantics.
Percentile widgets remain disabled until Datadog metadata confirms
percentile
support for the new distribution. Current gauges use no fill,
interpolation, or
`default_zero`; missing means unknown.
## Risk assessment
Moderate. The patch touches many database acquisition call sites, but
preserves
the selected physical pool and executes the same SQL on the acquired
connection. The main risks are incorrect semantic attribution,
cancellation
double-counting, and a helper accidentally acquiring twice. Typed APIs,
production-method PostgreSQL tests, source guards, the raw scrape
contract, and
the size-one-pool regression cover those risks.
No tenant, community, user, pubkey, event, channel, SQL, URL, pod,
version,
ReplicaSet, or request-controlled value is emitted as an application
metric
label. Deployment identity is supplied by infrastructure enrichment.
## Verification
- `cargo fmt --all -- --check` — passed.
- `cargo clippy -p buzz-db -p buzz-relay --all-targets --all-features --
-D warnings`
— passed.
- `cargo test -p buzz-db` — 122 passed, 0 failed, 263 ignored;
source-contract integration test: 3 passed, 0 failed.
- Focused relay compatibility, metric-contract, and readiness tests —
passed.
- `scripts/test-postgres-test-discovery.sh` — passed.
- Full `buzz-relay` package run from the identical tree reached 1,015
passes;
the six media-test failures all stopped in their shared local PostgreSQL
setup with `Sqlx(PoolTimedOut)` because Docker/PostgreSQL was
unavailable.
The same six failed in isolation, while every changed exact test passed.
- Exact implementation head:
`f92910b353086e9edf85918ca5f72190edbbe22f`.
- Exact multi-architecture staging image:
`dev-sha-f92910b353086e9edf85918ca5f72190edbbe22f-run-33607968668-1`
(`sha256:161712c8ed2e265a15df9b63e02248d5973481f875ff129d7d2ae78a09d487a2`).
- Focused staging GitOps PR:
<squareup/builderbot-platform-core-infrastructure#299>
— merged after renderer, inventory, infrastructure test, Kargo, Semgrep,
and
Intersect gates passed; the source/generated-artifact diff was exactly
two
image lines.
- Exact GitHub head reports 47 terminal checks: 35 successful and 12
intentionally skipped. PostgreSQL, unit, lint, security, both server
cross-compiles, backend integration, relay E2E, desktop, mobile, image,
Helm, Semgrep, zizmor, and DCO gates are green.
- Datadog readback identifies two exact-image pods,
`buzz-d79c8d8f7-ckv2l` and `buzz-d79c8d8f7-qzqdp`, in ReplicaSet
`buzz-d79c8d8f7`; both report the full source SHA above.
- Both pods report all eleven allowed pool/operation waiter pairs at
current
zero, with no invalid pair. The acceptance window observed nonzero
success
receipts for readiness, tenant resolution, authorization, subscription
history, event write, and maintenance, and no timeout, error, or
cancelled
outcome. Maximum observed wait was about 101 ms for maintenance and 50
ms
for reader subscription history.
The main **Buzz Startup & Rollout Safety** dashboard now has a live
Stage 2
database row with eight widgets and nineteen fully scoped queries. Final
readback preserved all seven top-level groups, found zero under-scoped
Row 6
queries, and confirmed the tracked-operation waiter boundary in the
panel
descriptions.
Generated with Codex.
---------
Signed-off-by: Ravneet Arora <rarora@squareup.com>
**Category:** fix **User Impact:** Wrapped channel and mention chips in the chat composer now align continuation text with the chip edge while keeping the icon on the first line. **Problem:** Plain composer decorations used absolute icons plus cloned icon-sized padding, so every wrapped fragment inherited an empty icon gap and the icon aligned against the union of all lines. **Solution:** Keep the icon in the first fragment's inline flow and restore normal chip padding on continuation fragments, while explicitly leaving the separate wrapping Buzz-link and sent-message rendering paths unchanged. <details> <summary>File changes</summary> **desktop/src/shared/styles/globals/composer.css** Scopes in-flow icon geometry and normal continuation padding to plain composer mention and channel decorations, excluding wrapping atom-link chips and preserving the human-icon vertical correction. **desktop/tests/e2e/mentions.spec.ts** Adds a rendered narrow-composer regression that checks two fragments, static icon geometry, first-line icon space, and continuation-line alignment to ordinary chip padding. </details> ## Reproduction steps 1. Open a channel in Buzz Desktop. 2. Narrow the chat composer enough for `#all-replies` to wrap. 3. Confirm the channel icon occupies only the first line and `replies` starts at the chip's normal left padding rather than an icon-sized inset. 4. Send or view a long inline Buzz chip in the message list at a constrained width. 5. Confirm its icon remains attached to the leading fragment and its remaining label continues cleanly on following lines. ## Screenshots **Composer — wrapped `#all-replies` channel reference**  **Message list — existing wrapped inline-chip rendering preserved**  Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
…ress (block#7254) Spec revision following two decisions: Option B admin deny (2026-09-02) and the HTTP ingress ruling (2026-09-02). Revises `docs/nips/NIP-FI.md` only. Follows block#7214 (merged spec v2). ## What changed ### Admin disconnect: session-only → deny-until-TTL The disconnect operation proceeds in two steps, in order: 1. Insert a memory-resident deny entry keyed by `(iss, target_pubkey)` with absolute expiry `until` — atomically combined with the `(iss, jti)` replay-identity reservation as one all-or-nothing mutation. If the deny set is at capacity (per-issuer bound), the relay rejects `503`; neither the jti nor the deny entry is recorded, and the caller may safely retry the same signed command. 2. Close all live WebSocket connections for the target pubkey, synchronously. The single atomic admission mutation (jti reservation + deny-entry insertion) lives inside `VerifyCommandJwt` step 7, after all pure authorization checks. The endpoint only closes sessions on success. This ordering ensures a capacity failure leaves no state behind and makes the retry-safe 503 contract implementable. The deny set is RAM-cache only — no durable storage, no schema changes. The same operational posture as the JWKS snapshot. A relay restart MAY forget active entries; the issuer SHOULD re-push still-active deny entries on observed restart (same publish/cache pattern as JWKS). If the issuer stops issuing assertions and re-push completes before any expired-entry reconnection attempt, residual exposure after restart is bounded by `max(0, min(exp, iat + maximum_assertion_age) - now)`. If the issuer continues issuing or re-push does not complete in time, that formula does not apply and access may continue beyond it. **`until` claim:** Required on the disconnect command JWT. Because an assertion accepted at the future-skew boundary (`iat <= now + skew`) remains valid until `iat + maximum_assertion_age`, the latest possible authority deadline is `now + skew + maximum_assertion_age`. The relay enforces `until <= now + skew + maximum_assertion_age`. A value above this ceiling rejects `400`; a past `until` still closes live sessions — absent an active same-key entry it creates no future denial, while an active entry remains unchanged under the merge rule. **Capacity and eviction (per-issuer):** The relay MUST bound the deny set size **per issuer**. Capacity exhaustion under one issuer MUST NOT cause rejection of another issuer's commands; the `503` capacity check is evaluated against the command's own issuer bound. Implementations MUST evict only expired entries; when an issuer's partition is at capacity and all entries are still active, the relay MUST reject the new command `503` without removing any existing entry. There is no LRU eviction of active denies. **Issuer-global deny:** The deny entry applies to admission across all communities served by the relay under that issuer. Identity-level revocation is intentionally not community-partial. **Cross-replica propagation:** In a deployment with multiple relay processes, the deployment MUST propagate both the session-close and the deny entry to every process serving admissions for the issuer's communities. The mechanism is deployment-defined (e.g. the existing inter-process message bus, same posture as JWKS convergence). Propagation is asynchronous with no protocol-level completion bound. The issuer re-push duty is the recovery path for lost propagation, exactly as for relay restart. **Response shape:** A successful disconnect responds `{"disconnected": true}` regardless of how many sessions were closed. No session count is returned; a count would aggregate activity across communities and constitute an information leak. **Admission procedure:** Step 5 registers the session's proven `k` before the deny-set check (new step 6) — ensuring any connection that straddles a concurrent disconnect is caught by one side or the other. `FI-TRACE-DENY-SET` oracle covers the per-issuer capacity rule and the straddling termination requirement. ### HTTP ingress enforcement Without explicit enforcement, a protected HTTP surface (bridge, invites, media, git) with NIP-98-only authorization allows a principal holding an active key to mint fresh NIP-98 events indefinitely — NIP-98 proves key possession only, not identity. Without assertion verification there is no expiry bound; the key remains valid for as long as it is accepted. **Pairing rule:** in enforce mode, a protected HTTP request MUST carry both: ``` Authorization: Nostr <base64-NIP-98-event> Nostr-Federated-Identity: Bearer <compact-JWS> ``` The NIP-98 pubkey MUST equal the assertion `nostr_pubkey` claim. Missing, mismatched, or invalid evidence of either kind denies, fail closed. **Verification:** reuses `VerifyAssertion` unchanged — offline, same JWKS, same claim requirements, same denial classes. **Per-request:** HTTP is sessionless; every request re-verifies. No session lifetime, no cached admission. The cumulative residual bound applies per request. **Deny-set applicability:** the deny-until-TTL entry introduced above is consulted per HTTP request identically to WebSocket admission. **Protected surface:** deployment-configured set of routes, fail-closed default (unclassifiable routes treated as protected). No normative route names in the spec. `FI-TRACE-HTTP-INGRESS` oracle added. Security considerations updated with HTTP ingress bypass analysis. NIP-98 source reference added. ### Other changes - `authorization_denied` rejection table row updated to "active deny-set entry for pubkey". - Discovery: `maximum_residual_upstream_revocation_seconds` remains `null` — the deny-until-TTL model is best-effort RAM state and provides no unconditional finite revocation bound. - Rejection and privacy: explicit sentence for HTTP denial path. - Client-attached transport: opening sentence generalized to cover both WebSocket and HTTP. ## Scope Single file: `docs/nips/NIP-FI.md`. No code changes. References block#7214. Channel: buzz-enterprise-identity-spec-v2 (#a6fe0b1c-987a-43c5-a974-71ee36678d78). --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
…elay slowness (block#7188) Three client gaps turn transient relay failures into permanent UI degradation. Under a slow or rate-limited relay: 1. A cold channel's profile batch exhausts its single retry and leaves raw npubs + broken mention chips until the user manually kicks the channel. 2. A thread opened from a notification trusts a successful-but-empty reply read as authoritative and never retries. 3. A rate-limited `CLOSED` on a history subscription immediately rejects the caller rather than retrying after the rate-limit window. All three are addressed without changing global query defaults or the happy-path behavior. ## Changes **Fix 1 — cold profile batch resilience** (`useUsersBatchQuery`, `desktop/src/features/profile/hooks.ts`) Override `retry: 3` with exponential backoff and error-gated `refetchOnWindowFocus: (query) => query.state.status === "error"`, scoped to this query only. The global defaults (`retry: 1`, `refetchOnWindowFocus: false`) are intentional for other queries and are unchanged. After the retry budget exhausts, a window-focus event (e.g. channel-switch) recovers the query automatically — but only when it is already in an error state, preventing unnecessary refetches for successful batches. **Fix 2 — stale-empty thread reads** (`useThreadReplies.ts`, `ChannelScreen.tsx`) Add optional `expectedEventId` parameter. When a completed paged fetch does not contain the expected event, throw `ThreadExpectedEventMissingError` so React Query's built-in retry machinery handles it rather than caching an authoritative empty. `ChannelScreen` passes `threadScrollTargetId` (the notification-linked reply ID) as `expectedEventId`. When notification routing changes `expectedEventId` while the same thread root is already mounted (same query key), an explicit `invalidateQueries` in a `useEffect` triggers a fresh validation pass. The `useEffect` is declared after `useQuery` so TanStack's internal options-update effect installs the new `queryFn` closure first; the refetch therefore uses the current `expectedEventId` rather than the previous null closure. For the cold-start race (target arrives before the first page returns), the effect detects `fetchStatus === "fetching" && status === "pending"` and calls `cancelQueries().then(invalidateQueries)` so the obsolete in-flight response cannot settle as authoritative before the new target's validation closure is active. The query-fn tracks consecutive fetch attempts per target. On attempt 3, it adds the target to `exhaustedTargetsRef` before calling `loadThreadReplies`. `loadThreadReplies` sees the target in the exhausted set and returns the fetched replies directly rather than throwing — the terminal attempt always resolves to success. No re-entrant scheduling: the resolution is synchronous inside the query function itself. Deleted/moderated targets never lock the thread in a terminal error surface. **Fix 3 — CLOSED recovery for history subscriptions** (`relayClosedRecovery.ts`, `relayClientSession.ts`, `relayClientShared.ts`, `relayGateBoundary.ts`) On a rate-limited `CLOSED` the subscription previously rejected the caller immediately. Store `filter` and `timeoutMs` on `HistorySubscription`, then on rate-limited `CLOSED` re-register under a fresh `subId` and defer `sendReq` until the rate-limit window clears — matching the live-sub recovery design already present in `relayClosedRecovery.ts`. Bounded to 3 attempts; exhausted retries reject immediately so callers are never left waiting indefinitely. A new op-timeout guards the retry REQ against a non-responding relay; when the op-timeout fires it sends `CLOSE` for the rotated `subId` (matching the behavior of the original timeout path) so the relay releases the slot rather than counting it against the per-connection cap. ## Tests - `relayClosedRecovery.test.mjs`: behavioral fake-clock tests for history-sub retry, 3-attempt exhaustion, op-timeout CLOSE send + late-EOSE non-regression, rejecting-`closeSubscription` swallowed without unhandled rejection, wiring source assertion (fails if `relayClientSession.ts` drops the `closeSubscription` callback) — 18 tests - `useThreadReplies.test.mjs`: `loadThreadReplies` unit tests (throw/exhaustion-guard); behavioral hook tests via real `QueryClientProvider` + `renderHook`: exhaustion-resolves-to-data, settled null→target change retries on missing-target page and lands target data (fails if `invalidateQueries` is removed OR if `useQuery` is moved after the `useEffect`), cold-fetch cancel-then-invalidate (gated fetcher — released after rerender, stale empty discarded, replacement fetch settles with target); ChannelScreen wiring source assertion — 9 tests - `profileBatchResilience.test.mjs`: source assertions for `retry: 3`, `retryDelay`, error-gated `refetchOnWindowFocus`, and unchanged global defaults — 2 tests --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary - add a persistent, community-scoped Bestie designation for local managed agents - surface the designated agent in the sidebar, agent library, profile actions, message toolbar, and draggable floating shortcut - bloom the floating shortcut into a lightweight compact composer that reuses the normal DM timeline, reactions, presence, and send behavior - support message handoff with a bounded snapshot and a full Buzz thread link so the agent can retrieve the complete conversation ## UX details - the floating avatar and expanded panel stay above app chrome and drag as one aligned surface - closing the expanded panel returns it to its top-right anchor - each mini-composer opening starts visually fresh while messages sent during that opening remain conversational - the designated Bestie's duplicate DM entry is hidden from the regular DM list - Bestie actions are suppressed inside the mini timeline to avoid recursive handoff ## Reliability and maintainability - preserve existing retention database paths across upgrades - serialize assignment and deletion, clearing matching assignments across community scopes before an agent is removed - fence async conversation resolution against workspace and assignment changes - validate stale assignments against existing local agents before hiding DMs - share lightweight assignment state across agent cards and keep protected-feature behavior out of the shared timeline API ## Testing - `pnpm --dir desktop test` — 5,886 tests passed - `pnpm --dir desktop exec tsc --noEmit` - `pnpm --dir desktop exec biome check ...` - `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets -- -D warnings` - focused native retention and Bestie assignment/command tests — 34 passed - `VITE_BUZZ_BESTIE=1 pnpm --dir desktop build:e2e` - `VITE_BUZZ_BESTIE=1 pnpm --dir desktop exec playwright test --project=smoke tests/e2e/bestie.spec.ts` - pre-commit and differential pre-push hooks ## Rollout The UI remains gated by the `bestie` build feature. Screenshots covering setup, empty, assigned, floating, and message-handoff states are included in the PR discussion. --------- Signed-off-by: Arjun Mahanti <arjun@squareup.com> Co-authored-by: Codex <noreply@openai.com>
…ges (block#7259) ## What Two new agent-facing capabilities in `buzz-cli`: ### 1. `buzz gifs` command group (agent KLIPY picker path) Agents can now search and share GIFs via the relay's authenticated KLIPY proxy without holding a provider credential. ```bash buzz gifs search # trending GIFs buzz gifs search --query "celebration" # search GIFs buzz gifs share --slug <slug> # report selection to provider Recents ``` Output is a JSON array of GIF objects. Paste the `cdn_url` field directly into `buzz messages send --content` — sending a GIF is a plain message containing the CDN URL, no special send-path handling. **Implementation details:** - Gates on NIP-11 `supported_extensions` containing `buzz-gif` and `gif.provider == "klipy"` - Uses relay-relative paths from the NIP-11 `gif` descriptor — no hardcoded paths; safe-path validation mirrors `desktop/src/features/gifs/api.ts` - New `post_json_authed` helper in `BuzzClient` handles NIP-98-signed JSON POSTs and 204 No Content responses - `customer_id` derived as `SHA-256(secret_key_bytes || '\0' || relay_url_bytes)[..16]` → 32 hex chars: stable, relay-scoped, not computable from public data, no storage needed - `locale` defaults to `$LANG` (stripped of encoding suffix) or `en_US` ### 2. NIP-30 custom emoji tags on outgoing messages `buzz messages send` now automatically attaches `["emoji", shortcode, url]` tags for any `:shortcode:` patterns in the content that resolve in the workspace palette — identical to the desktop composer behavior. ```bash buzz messages send --channel <uuid> --content "hello :wave: everyone :tada:" # → event carries ["emoji", "wave", "..."] and ["emoji", "tada", "..."] tags ``` **Implementation details:** - Hand-rolled single-pass scanner (no new dependency) implementing `:([a-z0-9_-]+):` case-insensitively with canonical lowercase output — mirrors `desktop/src/shared/lib/customEmojiTags.ts` exactly - Zero extra relay round-trips when content contains no `:` character; one `query` when candidates exist but none match - Palette fetch reuses the existing `union_custom_emoji` logic from `commands/emoji.rs` - `build_message` in `buzz-sdk` gains a new `emoji_tags: &[Vec<String>]` parameter (additive — all existing callers pass `&[]`); NIP-30 tag attachment lives in the SDK alongside `imeta` tags - MCP send path (`buzz-acp`) continues to pass `&[]` and is not affected; the MCP gap is noted in a comment ## Files changed | Crate | File | Change | |-------|------|--------| | `buzz-cli` | `src/commands/gifs.rs` | New — search + share handlers, NIP-11 gating, tests | | `buzz-cli` | `src/commands/mod.rs` | `pub mod gifs` | | `buzz-cli` | `src/lib.rs` | `Gifs(GifsCmd)` variant, dispatch arm, inventory test update | | `buzz-cli` | `src/client.rs` | `post_json_authed` helper | | `buzz-cli` | `src/commands/emoji.rs` | `scan_shortcodes` + `resolve_emoji_tags_for_content` + tests | | `buzz-cli` | `src/commands/messages.rs` | Emoji scan + tag injection in `cmd_send_message` + seam tests | | `buzz-cli` | `README.md` | `buzz gifs` section + emoji-in-messages note | | `buzz-sdk` | `src/builders.rs` | `build_message` gains `emoji_tags` param + tests | | `buzz-acp` | `src/pool.rs` | Update `build_message` call site (`&[]`) | | `buzz-acp` | `src/setup_mode.rs` | Update `build_message` call site (`&[]`) | | `countdown-bot` | `src/main.rs` | Update `build_message` call site (`&[]`) | Relates to: https://buzz.block.builderlab.xyz — buzz-team channel thread on agent GIF/emoji support --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Amend NIP-FI HTTP ingress with an explicit Git smart-HTTP credential-helper exemption. The exception covers method binding, endpoint-URL binding, and the `payload` tag requirement for `info/refs`, `git-upload-pack`, and `git-receive-pack`, while preserving per-request NIP-FI assertion, key pairing, and deny-map enforcement. The spec records Git's credential-protocol limitation, the required compensating controls, and the rule that this exception is limited to these endpoints and is superseded by per-request signing. Related: [PR block#7264](block#7264) Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
🤖 ## Summary In Buzz Desktop, choosing a multi-word name and immediately continuing a sentence could swallow the space after the mention: `Hey @alice Chenhello`. This keeps the separator, so the same action produces `Hey @alice Chen hello` without moving the caret or repairing the name by hand. The editor recognizes the complete selected label, including its internal spaces, and settles the autocomplete caret after the trailing separator. Deliberately moving left or clicking inside the label still lets you edit there; this is not a rule that forces every caret to the end of a mention. ### Related issue Independent base: `main`. Child: [block#7133](block#7133), whose disambiguated labels also contain spaces. Extracted from [block#7114](block#7114), retained as historical source (`98fe33ec`). [Behavior contract](https://github.com/block/buzz/blob/4fe451d9c251af59c34a0a890d38499912f7e3da/docs/mention-editor.md). Originating [Buzz discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848) · channel `f7a9536a-1738-4bad-a888-b3ea25010ef1`. ### Testing Select an existing member named Alice Chen, then type `hello` immediately. Repeat after ArrowLeft or clicking inside the mention: typing should follow your chosen caret position. Mock-browser captures, not live remote-agent evidence: #### Immediate typing preserves the separator Choosing the complete label then typing produces `Hey @alice Chen hello`.  #### Deliberate caret movement is respected After ArrowLeft, typing edits at the chosen caret rather than forcing the caret back beyond the separator.  [Original screenshot publication](block#7128 (comment)); immutable image URLs and captions retained here. #### Evidence and limitations **5,801 desktop tests**, **45 focused editor tests**, both new browser regressions, the browser-test build and static/type/size checks passed. [Applicable CI passed](https://github.com/block/buzz/actions/runs/33421534320). The broader browser run had **132 passes / 6 failures**: two clipboard-origin setup failures and four generic caret-formatting failures also reproduced on unchanged main. Full local `just ci` stopped at three native timing/probe failures; a same-head native rerun passed **3,005 tests** with 18 existing ignores. This is not a full local-CI pass. The change fixes insertion and caret behavior, not duplicate-name recipient selection, discovery or invitation. Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
🤖 ## Requested rebase published — ef40744 Rebased onto fetched main **47d068e2109d077414cbf2f4f1c927f6d051037a**, published **ef40744b3aeb4baaf8c81416e1a644fb5b315f91** with the exact expected-old `df7fad6a` force-with-lease. No merge. Manual conflicts were additive: preserve main's exact-key identity documentation alongside the availability contract, and retain both Bestie props and the shared availability reader in `UnifiedAgentsSection`. Range-diff confirms unchanged lifecycle policy: exact-key action-time authority, Unknown versus Offline, rejected shutdown retains record/memberships, and separate local/provider/owner gates. Main's exact-key profile routing survives. Both test-only CI synchronization repairs (natural toast expiry and bounded stderr wait) are byte-identical to the prior head. All ten original authors/messages/DCO/material coauthor trailers are preserved; configured signing policy was not changed. Fresh checks on the rebased candidate: - TypeScript, Biome on 26 changed TypeScript files, differential file-size gate, and diff whitespace: pass. - Focused production-hook/card/profile units: **58/58**. - Fresh E2E build, availability/deletion browser: **11/11**, no retries. - Main exact-key profile cases plus failed-DM send/startup retries: **6/6**, no retries. Previously reviewed full Desktop/buzz-agent package and mutation evidence is reused for unchanged behavior; no ceremonial full suite, new native/provider test, or `just ci` pass is claimed. Local configs, dependency links, and historical artifacts are preserved. Hosted observation: **MERGEABLE**, **BLOCKED / REVIEW_REQUIRED**, no new-head formal review. [CI 33699735990](https://github.com/block/buzz/actions/runs/33699735990) is running (including Rust and Desktop lanes), not a completed success. DCO and required Security aggregate passed at the observation; the separate Codex advisory review was skipped. No completed failing check or new inline feedback observed. Historical approvals are not new-head approvals. No reviewer/security authorization or merge action was performed. --- ## Feature summary and retained pre-rebase evidence ## Summary In Buzz Desktop, an agent could look online just because it had been started or deployed, even when there was no current sign it was connected. Cards and profiles now show availability from the agent's relay presence rather than a saved launch record, so you can distinguish an online agent from one that was merely deployed. Agents cards and profiles use presence reported through the shared server (the relay). A successful presence read with no online agent shows Offline; failed/disconnected evidence shows unknown, rather than retaining a misleading cached Online state. Lifecycle actions remain separate. An offline agent may still have a Shutdown action because the deployment record exists. Shutdown reports a **request**, not proof the process stopped. Offline does not imply that starting a duplicate agent is safe, and Online does not promise a response. ### Related issue Independent base: `main`; no stack parent or child among the replacements. Extracted from [block#7114](block#7114), retained as historical source (`98fe33ec`). [Behavior contract](https://github.com/block/buzz/blob/f4bb2ed44e5a989d93c5f51e93c0bbd2dca941be/docs/agent-availability.md). Originating [Buzz discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848) · channel `f7a9536a-1738-4bad-a888-b3ea25010ef1`. ### Testing The same saved provider-backed agent, with only authored presence changing. These are mock-browser states, not a before/after deployment or live relay transport test; production UI is unchanged by the later fixture repairs. **No online presence:** gray dot, existing Shutdown control retained.  **Online presence:** green dot, same lifecycle control.  [Capture details](block#7127 (comment)). To check manually, compare runtime-only transitions with presence updates, then disconnect/fail the presence read and verify it does not stay Online. A Shutdown request should not immediately claim confirmed termination. #### Historical pre-rebase evidence and limitations (df7fad6) Lifecycle production source remains **`f4bb2ed44e5a989d93c5f51e93c0bbd2dca941be`**. Current published head is **`df7fad6ae65dda78508317186a95522d1bb22ed9`**: the prior browser synchronization at `b78d093e` plus an additive two-file Rust test-harness synchronization described below. No production bytes, dependency/configuration files, or prior commits were changed; no rebase. Current live main `0dbd036f5bff33e7ade75e7639f3218d424a6e73` has identical failing-test/toaster/send-flow source; the causal browser comparison used latest successfully tested main `04babf02655440b4dfd37f2e2df605ead0a030d8`. **Lifecycle/deletion correction:** both Agents and actual profile deletion now pass the shared exact-key availability reader, not raw cached data. It reads the canonical query state and connection at action time, including after awaited channel discovery. Failed/disconnected/pending evidence and unqueried persona siblings are unknown; successful missing means Offline only for a requested key. Successful background refetch cache remains usable; settled failure revokes it. No second cache or per-row polling was added. Provider record + channel + Online/Away/**unknown** awaits shutdown submission before local removal; rejection preserves record/membership for retry. Established Offline preserves intentional no-request removal. No route preserves warned local removal. Local agents retain native stop-before-remove, independent of presence. Profile consent now describes a shutdown **request**, not remote deletion or guaranteed termination. Existing ownership and force gates are unchanged. **Verified, reused exact-candidate validation:** the independently approved eleven-file patch (SHA-256 `2f69fe12ef0420e62dea1fd8db28cfa22cde5eecaf8080e656310a3e60d0cf86`) was committed without byte changes. Desktop **5,921 passed, 0 failed/skipped**, including **26 new mounted production hook/IPC regressions**; rebuilt availability browser suite **11/11 passed, no retries**, including four actual profile Delete journeys. Desktop check (existing 4 warnings/5 infos), typecheck, production/protected-feature artifact matrix, differential file-size/policy and diff checks passed. No blanket rerun or new full-repository `just ci` is claimed for this frontend correction. Production regressions cover cached Online **and Offline** failure/disconnection, genuine missing/Offline, pending, successful inflight refetch versus settled error, retained reader, error during awaited channel discovery, unqueried persona sibling, shutdown rejection/order/cancel, no route and local authority. Browser fixtures use safe mock IPC and a retained provider receipt, not a real deployment. Three restored mutation controls fail: unknown → skip shutdown (**15** regressions), Agents raw-cache reader (**6**), actual profile raw-cache caller (**1 browser journey**, false removal on failed cached Offline). Independent review approved the exact frozen bytes and added **4/4 cached-empty failure/disconnection probes** across both callers. This is local independent approval, not formal GitHub/A Team clearance. The prior native propagation/poll-count defects remain closed ([earlier response](block#7127 (comment))). The prior hover-popover correction at `b55423f6` remains covered by the full 11-journey browser run: pending/failed/disconnected means no badge or accessible status, genuine missing/Offline retains an Offline badge. Its earlier fallback-restoration mutation failed as expected (badge count 1 rather than 0); that historical witness is reused, not rerun. **Reused unchanged native/system boundary:** local `just ci` at `c59067d8` passed workspace/Tauri fmt/clippy, static/policy checks, Rust unit recipe, native workspace **3,159 passed / 20 ignored**, Web build and **2,019 mobile tests**. No native implementation changed in this lifecycle correction. These are historical boundary results, not new-head native/live certification. [Parent CI](https://github.com/block/buzz/actions/runs/33650549130) passed with **14 retry-recovered browser flakes**, not retry-free. Old-head CI/reviews are not current-head clearance. **Hosted gates:** [CI33662151103](https://github.com/block/buzz/actions/runs/33662151103) on `f4bb2ed4` **FAILED**: smoke shard1 had 322 pass, one failure, one retry-recovered flaky, two skipped. The failed first-DM retry test timed out on all three attempts because the error toast intercepted Send. That failure is preserved, not waived; the scoped test repair below is published as `b78d093e`. [CI33668171165](https://github.com/block/buzz/actions/runs/33668171165) on `b78d093e` subsequently **FAILED** the Rust unit budget regression described below. Both original failures remain visible; neither was retried to green. Exact `f4bb2ed4` and `b78d093e` APPROVED reviews cover unchanged reviewed bytes, not formal approval of the new head. Fresh exact-head CI and the established automated technical rereview are the next gates for `df7fad6a`. Historical deletion responses remain ([5092381800](block#7127 (comment)), [5092391193](block#7127 (comment))). No formal review dismissed. The [security notice](block#7127 (comment)) and latest-push maintainer/codeowner policy remain separate actionable gates: eligible Block organization members own current-range authorization. No merge/security authority exercised. **CI causal repair (`b78d093e`, test only):** the error `Message failed to send: Mock first DM send failed.` is deliberately injected by the existing fixture. CI screenshot and retry trace show the bottom-right Sonner notification over the actual enabled Send button. `fill()` leaves the pointer parked there; Sonner pauses its 4-second lifetime while hovered. A fast run can click before animation settles (unchanged local test passed in 2.7s; two actual tested-main CI cases passed first attempt in 3.3s), which does not disprove the failure. Independent controlled browser runs on `f4bb2ed4` and tested main `04babf` both reproduced the same toast hit-test at Send `(1203,627,32,32)`, persistent hover beyond 4s, and intercepted ordinary click with no second send. Moving the real pointer to the editor allows natural expiry and successful ordinary retry, preserving all original DM-channel/recipient assertions. This same synchronization already exists in the neighboring agent-startup-failure test. The one-file correction keeps the visible error assertion, scopes its toast locator, moves the pointer back to the editor and observes normal toast removal (bounded 10s) before retry. No forced click, direct toast dismissal, mocked clock, CSS override, skipped test, production behavior change, or new backend mock. Six focused browser executions pass (first-send/startup-failure, three repeats each, no retries); the held-toast control fails on original bytes at the Send click while the exact repaired test passes. Biome and diff checks pass. Reuse unchanged 5,921 Desktop / 11 availability browser / four independent probes above; no semantic production change warrants repeating those suites. Original failed CI attempt/retries, local fast pass, deliberate failing control and all traces remain in `WORK_LOGS/AVAILABILITY_CI_B9210A40`. Browser evidence is mock-IPC Chromium, not native/live-relay certification. The UI still temporarily overlays Send while a notification is hovered; the test exercises its real move-away/expiry recovery, not immediate click-through. **Rust CI causal repair (`df7fad6a`, test only):** [original Rust / Unit Tests failure, job100375291370](https://github.com/block/buzz/actions/runs/33668171165/job/100375291370) tested GitHub merge `223dee91a396d8cb4ebf18b9b8559e5a54951235`. `context_recovery_budget_exhaustion_surfaces_the_error` failed at `regressions.rs:2756` in **0.091s** because its immediate stderr snapshot lacked `context recovery budget spent`. ACP context-error assertions had already passed. The captured prefix shows all three budgets **32768 → 16384 → 8192 bytes**, above the 4096-byte floor, and ends during the third attempt. This is **not evidence of floor exhaustion**. The collector is an independent Tokio task; a stdout response is not a stderr barrier. Recovery, harness and test blobs were identical across the compared base/head/merge parents; no production regression was implicated. The shared test Harness now provides a bounded event/condition wait, registering for collector notifications before reading the buffer to avoid lost wakeups. The budget and adjacent terminal floor assertions wait for their own diagnostic and retain the matching snapshot. The budget test still requires the provider's ACP context error and exactly three recovery rungs, now corroborated by **exactly four provider calls** and no floor diagnostic. Timeout remains a real failure with captured stderr. No fixed sleep, weaker assertion, skip, provider-limit/logging change, dependency/config edit, or production change. **Deterministic causal control:** the same real agent/HTTP-provider/ACP scenario holds only stderr collection behind a one-shot gate until after stdout responds. The old immediate snapshot fails the original budget assertion (intentional exit101); the repaired wait explicitly remains Pending while held, then passes after release. No scheduler-speed assumption or fixed sleep. This reproduces the observation race under controlled delay, **not the exact historical CI schedule**. A missing-diagnostic test proves the wait actually times out. Original failure and deliberate failing-control logs/patch remain in `WORK_LOGS/RUST_TRIAGE_06E32D2D` and `WORK_LOGS/RUST_SYNC_BC5758B9`. **Final candidate validation:** focused recovery **9/9**, floor **1/1**, absent-diagnostic timeout **1/1** pass. One full touched-package run, `cargo test --locked -p buzz-agent`: **695 passed, 0 failed, 1 existing ignored**, including all **54 regressions**. Local nextest was unavailable, so this uses the repository-supported cargo-test fallback, not a claim of nextest reproduction. `cargo fmt --check`, package-scoped Clippy all-targets with `-D warnings`, differential file-size/policy and diff checks pass. Previously reviewed availability production and the Desktop/browser evidence above are unchanged and reused; no all-native blanket rerun. The test-only delta was self-reviewed against collector ordering, timeout and falsification evidence. Existing production approval remains valid for those bytes; exact-new-head technical/CI clearance is not assumed. The required **Security aggregate** is distinct from optional Codex advisory feedback; no security authorization, human review contact, or merge was requested. **Remaining policy limits:** shutdown submission is not harness acceptance or process termination; confirmed Offline/no-route local removal may leave a remote process; route discovery is best effort, membership cleanup uses `Promise.allSettled`, and multi-instance deletion is sequential/non-atomic. No distributed singleton, provider-health, tenant-switch cancellation, live relay TTL or packaged WebView/VoiceOver certification is claimed. The pre-existing DM-header raw-presence fallback (`ChannelScreenHeader`/`useActiveChannelHeader`) remains outside this repair and uncertified. Screenshots above remain historical mock-browser illustrations, not new deletion or native transport evidence. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Unify presentation-only cloud provenance across agent identity surfaces. Keep successful local-inventory and verified-ownership gates; preserve main availability and mention spacing behavior. Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Signed-off-by: Logan Johnson <loganj@squareup.com>
## Summary Cuts perceived agent-mention send latency by publishing the message first and waking the agent afterwards, instead of blocking the send on a synchronous agent start/deploy round-trip. A send that mentions a stopped or undeployed managed agent now shows the message immediately; the wake runs fire-and-forget after the relay accepts the publish. The already-running-agent send also gets faster via revalidation dedupe and NIP-11 caching. ## Changes ### Publish-first agent wake - Wakes for mentioned managed agents are collected during send preparation and flushed fire-and-forget only after `await send(...)` resolves. No start can fire — and no "your message was sent" toast can appear — for a message the relay never accepted; every abort path (cancel, readiness error, publish rejection, dismissed non-member prompt) simply drops the queue. Persona-create wakes ride the pending draft behind the non-member prompt for the same reason. - Each wake is bound to the tenant scope captured at send time: the new `useDetachedAgentStart` hook passes `expectedRelayUrl` + `expectedSignerPubkey` with every start, so a wake that outlives a community switch fails closed at the backend instead of spawning against the new tenant. A wake whose scope has not resolved yet (identity query still loading, blank stored relay URL) is refused with a recoverable toast rather than fired unscoped. - In-flight wakes are deduped through a module-level map keyed by `(relay URL, pubkey)` — the same tenant pair the backend keys on — so two quick sends or two composers cannot double-spawn a cold agent during the seconds-long start window. Entries are deliberately retained across community switches (the key *is* the tenant scope, so a retained entry can never affect another community, and clearing it let an A→B→A round trip deploy a provider agent twice) and self-clean when the start settles. - Wake-failure toasts are fenced to the community they fired in via a module-level scope mirror: a start that settles after a community switch logs instead of rendering community A's failure over community B's UI, and an A→B→A return re-delivers the warning where it is actionable. - Membership attach and access-policy writes stay synchronous, so the harness's first kind-39002 read still sees the channel. ### Replay floor - The send timestamp travels with the wake as `BUZZ_ACP_REPLAY_FLOOR`, threaded through both local spawns (`spawn_agent_child`) and provider deploys (`deploy_to_provider` injects it into `launch.policy_env`), so the harness's startup watermark replays back past the just-published triggering message no matter how long the spawn takes. `buzz-acp` clamps the floor to `[now − 15min, now]`. - The floor is captured at enqueue time, not flush time — the flush runs post-publish, so a flush-time stamp could exceed the message's `created_at` and skip the very message the floor exists to cover. - On local spawns the caller's floor is asserted *after* the user env layering (and the ambient parent-process value is stripped unconditionally), so a saved persona/global/agent env entry cannot shadow this send's floor — mirroring the shadow-strip the provider path applies to `launch.env`. Both halves share one `REPLAY_FLOOR_ENV_VAR` const. ### Send-path latency reductions (already-running agents) - Mention revalidation is deduped: the publish-boundary pass reuses the pre-side-effect authorization pass unless an awaited round-trip actually separated the two (background upload, link-preview settlement, DM expansion, a real access-policy/membership write, or active-huddle enrollment). This preserves the block#5681 authorization boundary while making the common send single-pass. - NIP-11 `self` lookups are cached per relay URL for 5 minutes. Only verified values are cached — non-2xx and malformed responses stay retryable — and URL keying keeps community switches from serving another relay's identity. - `applyReusableAgentAccessPolicy` now reports its relay write explicitly (`{ agent, wrote }`) instead of signalling through object identity, so the revalidation trigger above is load-bearing by construction. ### File splits Four files crossed the repository file-size ratchet during this work; one cohesive unit was extracted from each rather than raising a ceiling — `runtime/setup_payload.rs`, `commands/agents_create_fields.rs`, `app_state_accessors.rs`, and `useEnsureAgentMentionsReady.ts`. The ratchet is green at the tip. ### Review follow-ups The three concrete findings from the first review round are fixed at the tip: the pre-publish wake and its false "your message was sent" toast (fixed by queueing wakes behind the publish), the stale cross-community failure toast (fixed by the scope-mirror fence), and the A→B→A duplicate provider deploy (fixed by retaining the tenant-keyed in-flight entries across switches). The fast-path admission-staleness point is answered in the review thread: deferred paths already re-validate at the publish boundary, and the remaining fast-path window is milliseconds against an irreducible network-transit race. Mid-branch send-perf instrumentation was added to attribute the residual spinner latency and reverted once that analysis concluded — it is net-zero in this diff. ### Deferred follow-ups Durable mention catch-up via `event_mentions` (option 2 step 3) and backend deploy-epoch coalescing for the wake paths that do not funnel through `useDetachedAgentStart` (Agents-panel Start, restore, inbound-persona deploys) are intentionally left for separate changes. ## Testing - `cargo test --lib` on desktop/src-tauri: 3054 passed; clippy `-D warnings` + fmt clean - Desktop unit tests: 5856 passed (the 5 failures are the pre-existing `inboxReopenNavigation` / `useRetainedProjectGitViews` baseline, present on origin/main); `tsc --noEmit` and biome clean - Full mentions (87), channels (89), and community-rail (25) Playwright smoke suites against `pnpm build:e2e` bundles, with 3× stress reruns of each new spec - The load-bearing regression specs were confirmed red on the pre-fix code: publish-failure → zero starts and no false toast, the dedupe hold (1 call vs 2), the fail-closed scope refusal, the rail-switch toast fence, and the A→B→A retention spec (1 deploy vs 2) - New unit coverage pins the queue contract (enqueue-time floors, attach-seam queueing), the scope capture and verbatim relay-URL handoff, the dedupe map's keying and settle-then-repermit behavior, the unscoped refusal, the toast-scope mirror, the `{ agent, wrote }` contract, and the replay-floor env layering on both spawn paths 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary - add iOS and Android voice-note recording and preview directly in the mobile composer - add waveform playback, scrubbing, speed controls, haptics, and one-shot playback in chat - package recordings in a canonical H.264/AAC MP4 envelope on both platforms so existing relays accept them - preserve the shared composer interaction and attachment-card treatment across mobile platforms Mobile counterpart to block#6978. ## Testing - `just ci` - `just mobile-check` - `just mobile-test` (2,026 tests) - Android debug build compiled, installed, launched, and Voice note verified in the attachment menu on Pixel 10 - signed iOS device build installed on iPhone --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz>
## Summary - show status and huddle emoji beside names in DMs and message rows - provide accessible tooltips, fallback status emoji, and profile-menu icon replacement - add the desktop status editor with preset durations, a ShadCN calendar, and a capped half-hour time menu ## Validation - desktop checks, typecheck, and file-size guard - 5,802 desktop tests - focused Playwright coverage (3 passed) - E2E build and native Builderlab staging verification Updated visual snapshots are attached in the PR comments. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
…st timing (block#7270) ## What Two remaining E2E hardening fixes from the Desktop Smoke flake pattern introduced by `ac5a18697` (Bestie — added `VITE_BUZZ_BESTIE=1` to `.env.e2e` and mounted `BestieGlobalOverlay` globally). The toast/DM-retry fix landed independently in main via block#7127 (`input.hover()` + bounded `toHaveCount` wait); that hunk is dropped from this PR. ## Fixes ### `agent-control-regressions.spec.ts:240` — Stop does not accept an unconfirmed or foreign-channel result **Cause:** Playwright 1.60.0's `page.clock.install()` fakes all timers including `requestAnimationFrame`. The test called it before opening the settings menu. With RAF frozen, the `DropdownMenuContent`'s `zoom-in-95 duration-150` CSS enter-animation never advances — Playwright's stability check observes a continuously-changing bounding box until the 30s test timeout. **Fix:** Re-sequence so the menu is opened on real time first. After `openAgentActivity`, open the trigger, assert visibility/enabled, call `waitForAnimations(page)` to settle the enter-animation (real `setTimeout`, no fake clock installed yet), then install the clock. The `fastForward(8_001)` correlation timeout still works because it's scheduled after the clock is active. Pointer actionability preserved — normal `stop.click()` (no `force`) fails with pointer-interception under a covering surface. ### `message-feedback-snapshots.spec.ts:97` — profile hover uses the channel hover surface **Cause:** `channel.hover()` triggers a CSS `transition-colors` animation. With the Bestie `LayoutGroup` mounted, `evaluate()` captures a mid-transition background value that never matches the profile card's settled token. **Fix:** `waitForAnimations(page)` after `channel.hover()` and before reading `channelHoverColor`. ## Evidence - Target specs pass: stop-turn 8/8, profile-hover passes. - Full `agent-control-regressions.spec.ts` (7 tests) green. - `just desktop-typecheck` clean at pushed head `0fbfe2a9f`. - No production code changed. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
## Summary - Let Markdown tables use the available message width instead of their maximum-content width, inheriting the renderer's existing `wrap-anywhere` handling for long tokens and links. - Top-align header cells as well as body cells. Give cells a `min-w-24` readability floor so short labels do not collapse into one-letter columns; retain the existing table-local scrollbar when many columns genuinely cannot fit. - Preserve semantic table markup, links, inline code, and surrounding message layout. Add three browser regressions through the real message renderer (channel, narrow thread, and many-column overflow). ### Related issue Fixes block#5313. No matching open table-readability PR found. Owner-authorized task channel: `819b36d5-7371-4ed0-bf9b-d461d723779e` Source: buzz://message?channel=819b36d5-7371-4ed0-bf9b-d461d723779e&id=3bf2d1bd5df28fcb481e71159dc1be2b3888918b05f67e270354fbfea65a8c90 ### Testing Original production candidate: `4427f2e6f9ef85a01a267fe9eabd1cf13cacfb78`; base: `7a9a5233d9d755e715be0c585cf7850e935d28cf`. - `pnpm -C desktop check` — passed. - `pnpm -C desktop test` — 6,110 passed, none skipped. - `pnpm -C desktop typecheck` — passed. - `BUZZ_PROTECTED_BUILD_OUTPUT=<isolated-production-directory> pnpm -C desktop build` — passed. - `pnpm -C desktop build:e2e --outDir <isolated-candidate-directory>` — passed. - `just file-size-check` and `git diff --check` — passed. - Chromium / mock Tauri bridge: all 3 new Playwright tests passed against the fixed candidate build. Both wrapping tests fail on the unchanged base build, while the overflow-fallback test passes. Each browser run used a unique output directory and a non-reused local server pinned to its build directory. - At 883px channel message width: table scroll width **1,395 → 883px**. At 292px thread message width: **1,395 → 292px**. All cells top-aligned; long tokens/URLs wrap, link destinations and code text remain intact, short labels stay on one line, and document width remains 1,280px. - Focused fresh-frame review checked actual before/after screenshots and the complete diff. It caught one-letter label wrapping in an early width-only candidate; the final cell-width floor and regression assertion address that finding. Limitations: native Tauri/WebKit and relay-backed integration were not exercised for this CSS-only delta. Repository-wide `just ci` was attempted but exceeded the local command budget during unrelated Rust compilation; it is **not** reported green. The full affected TypeScript package gates above passed on the exact candidate. Production/E2E builds retain existing chunk-size and mixed static/dynamic import warnings. Before/after screenshots are posted below using the repository's screenshot publication script. No merge, production installation, or runtime restart is requested. ### CI-driven test-only follow-up Current head: `925d964b6cf31ba704d74baf73769110774b2789`. Smoke shard 3 exposed an older test in `messaging.spec.ts:457` that still required three columns of ordinary prose to overflow horizontally ([failure log](https://github.com/block/buzz/actions/runs/33771325404/job/100702242706)). This expectation contradicts the intended wrapping change. Updated its name and assertion to require containment; the separate many-column local-scroll test is unchanged. No production code or harness settings changed in this follow-up. - Affected browser validation: **4/4 passed** (updated existing prose/narrow table case plus the three new channel/thread/overflow cases). - Reused the immutable `4427f2e6` E2E build on a fresh non-reused server; production source is unchanged at the current head. The package/build checks above remain evidence for that unchanged production tree, not a claim of rerunning the full suite at the new SHA. - Targeted Biome check, file-size gate, and `git diff --check` passed. - Current-head CI: https://github.com/block/buzz/actions/runs/33773490446 - Prior unrelated Bestie baseline failure and reproduction: block#7279 (comment) - Security authorization and review must target the **current** head, not the old screenshot/build SHA. A Block organization member must comment exactly `@buzz-security-review 925d964`. --------- Signed-off-by: Logan Johnson <loganj@squareup.com>
…block#7278) Adds the media/Blossom possession-proof exception section to \`docs/nips/NIP-FI.md\`. ## What this changes Encodes the condition set confirmed by Thufir's security review (2026-09-03) as normative MUSTs in NIP-FI. Kind-24242 Blossom auth events are admitted as the NIP-FI pairing possession proof for **media routes only** — a named, bounded exception with an explicit precedent fence against expansion. ### Scope fence Kind-24242 proofs are valid only on: | Proof type (`t` tag) | Valid route | Method | |---|---|---| | `upload` | `PUT /upload` (and temporary alias `PUT /media/upload` until removed) | PUT | | `get` | `GET /media/{hash…}`, `HEAD /media/{hash…}` | GET, HEAD | All other protected routes MUST reject kind-24242 proofs. ### Upload proofs Exactly one `x` tag over consumed body bytes; temporal check precedes body consumption. ### Read proofs (the relaxation Will approved) Host-wide MAY: no `x` required. Exactly one `server` tag matching the resolved tenant host is a MUST. Optional `x` must match parent hash if present. Named residual (verbatim in spec): within at most 60 seconds from minting (plus 5s future-skew), a captured full header set allows reading any media blob on exactly one tenant host — read-only, membership-checked, revocable, not state-changing, not cross-tenant. ### Freshness (Thufir option 2) - `created_at <= now + 5s` (bounded future skew) - `now - created_at <= 60s` - Exactly one `expiration`, valid at admission, satisfying `expiration <= created_at + 60s` ### Transport/cardinality Exactly one each of `Authorization` (Nostr scheme), `t`, `expiration`, `server`; `x` at most once; reject any duplicate, malformed, or conflicting instance. ### Per-request pairing Full assertion verification, exact key equality between assertion `nostr_pubkey` and kind-24242 signer, deny-map enforcement on every request. Stub gap named. ### Compliance note PR block#7264 implementation is explicitly non-compliant until the bounded hardening task lands (named gaps: multi-tag acceptance, 3600s window, optional `server`). ## Behavioral oracle `FI-TRACE-HTTP-INGRESS` extended to cover kind-24242 admission and denial cases. ## Scope Docs-only. No code changes. The code hardening is a separate follow-on task. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Why Early relay failures can currently appear as a container restart without a trustworthy in-process account of whether crypto, structured logging, configuration, relay identity, or the metrics listener failed. Most of those steps happen before the Prometheus exporter exists, so their chronology belongs in logs rather than metrics. Implements the logs-only early-startup slice of block#7238. Post-bind Prometheus exporter supervision is tracked separately in block#7284. ## What changed - create a process lifecycle recorder before the Tokio runtime and emit a fixed, versioned JSON schema directly to stderr; - record started and exactly one terminal event for `crypto_init`, `tracing_init`, `config_load`, `key_load`, `metrics_bind`, and the aggregate `process_telemetry` phase; - keep every status and reason bounded and suppress raw errors that could contain credentials, keys, URLs, or other secrets; - return typed metrics-install errors so `metrics_bind` can be classified without logging raw values, while preserving the existing public `metrics::install` API; - document the logs-only evidence contract and add real child-process regressions for success and failure paths. This PR adds **no startup metric families** and no dashboard contract. Existing application metrics remain unchanged. ## Verification Exact head: `8faf7526822a119efa035e58b2b3c59aa67fc81d` - `cargo fmt --all -- --check` - `cargo clippy -p buzz-relay --all-targets -- -D warnings` - relay binary target: 13 passed, 1 PostgreSQL-only test ignored - real relay child-process lifecycle target: 9 passed - full relay package library target: 1,023 passed, 89 ignored; the same six media tests failed at `crates/buzz-relay/src/api/media.rs:1145` with `Sqlx(PoolTimedOut)` because local PostgreSQL is unavailable - three independent exact-head reviews found no correctness, security, compatibility, lifecycle-accounting, logs-only-scope, or test-adequacy finding All exact-head GitHub CI gates are green, including lint, unit tests, PostgreSQL, relay/backend/desktop integration, both Linux server cross-compiles, Windows/macOS builds, and security checks. ## Staging verification - exact multi-architecture image: `dev-sha-8faf7526822a119efa035e58b2b3c59aa67fc81d-run-33708188952-1` - immutable manifest: `sha256:26cad28266a6bb0b0e7081eb6091d374e5489f8bb78c475a4a65737dee86cc67` - image workflow: https://github.com/block/buzz/actions/runs/33708188952 - focused staging deployment: squareup/builderbot-platform-core-infrastructure#314 - replacement ReplicaSet `buzz-d68764bc7` has two Ready pods with zero restarts - Datadog received one complete, contiguous sequence 1-12 from each pod; both end with `process_telemetry/terminal/succeeded` at 3 ms - queries scoped to the replacement ReplicaSet return no data for the removed `buzz_startup_phase_terminal` or `buzz_startup_phase_duration_seconds` families The experimental Row 7 was removed from the Buzz Startup & Rollout Safety dashboard. This logs-only PR deliberately adds no replacement dashboard row. --- **Update Sep 3, 12:26 ET:** Clarified the review boundary: this PR does not close the broader block#7238. Later exporter-task termination is pre-existing runtime behavior and is now explicitly tracked in block#7284; no production code or staged image changed in this update. Generated with Codex Signed-off-by: Ravneet Arora <rarora@squareup.com>
Brings in 58 upstream commits (block/buzz) since eed74bd, including the NIP-OA authorization time bounds fix (block#7004), the relay acknowledgement frame fix (block#6961), thread-scoped agent sessions (block#6732), and the harness-agnostic effort write path (block#4625). Five conflicts, resolved to keep both sides rather than pick a winner: crates/buzz-acp/src/queue.rs Upstream repartitioned the queue from per-channel to per-scope (SessionScope) to support thread-scoped sessions. Our DropCounts accounting is orthogonal to that, so it is preserved and re-expressed against the new scope keys; the depth-cap and dedup warnings now carry both the scope label and the running total. Also restores the doc comment on pending_channels, which our earlier change had displaced. crates/buzz-acp/src/pool.rs build_message gained an emoji_tags parameter (NIP-30, block#7259). Our post_notice keeps passing its mention list and adds the new argument. crates/buzz-acp/src/lib.rs Upstream extracted the inbound author gate into authorize_normal_listener_event / InboundAuthorGate, which improves attribution via effective_prompt_author but logs a refusal at debug and posts nothing. Taking it wholesale would have reverted the fix in #2 and made refused requests silent again. The gate's structure and author resolution are adopted; the one-notice-per-(channel, author) reply and owner mention are restored around it, capturing the notice inputs before the gate consumes the event. desktop/src/features/agents/ui/OtherSetupAgentMarker.tsx Upstream rewrote the label to "Not managed on this device", which no longer names the product. Taken as-is; the rename is no longer needed. desktop/src/features/profile/ui/UserProfileAgentManagementRows.tsx Upstream rewrote the remote-deletion copy with clearer semantics. Taken, with Buzz -> Dreamforge applied to the new wording. kind.rs auto-merged cleanly and was checked by hand for a numeric collision: upstream's only new kind is KIND_HUDDLE_LIVENESS (48104), clear of our ticket kinds (30623, 30624). Verified: cargo check clean; cargo test -p buzz-acp -p buzz-core gives 885 passed / 29 failed, and a control run of pristine upstream/main in a separate worktree gives the identical 29-name failure set, so the merge introduces no regressions (those tests are timing-sensitive and fail under load). tsc --noEmit clean. Dreamforge branding intact: the remaining "Buzz" occurrences in renamed files are identifiers and comments, which brand.ts deliberately excludes from the rename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The upstream merge adopted `InboundAuthorGate` wholesale for logging while restoring only the in-channel notice, which left two observability regressions against #2: - The author-gate refusal logged at `debug!` again. The notice reached the sender, but the line the owner would grep for did not exist in practice. Restored to `warn!`, with the reasoning comment. Because it now lives in the gate rather than one call site, both production listeners are covered — the fork's original only covered the normal listener. - The no-rule drop lost its `channel_id` and `kind` fields, leaving a bare message that says something was dropped but not what or where. The event is moved into the gate before that point, so the fields are captured alongside the notice inputs and logged from there. cargo check clean; cargo test -p buzz-acp unchanged at 885 passed / 29 failed, matching the pristine upstream/main control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the fork current with
block/buzz— 58 upstream commits sinceeed74bde2.What lands
Notable beyond routine fixes:
e47690c21Enforce NIP-OA authorization time bounds (Enforce NIP-OA authorization time bounds block/buzz#7004) — security fix on the auth-tag mechanism our managed agents depend on2f3dd850dfix(relay): reject a frame on its own acknowledgement channel (fix(relay): reject a frame on its own acknowledgement channel block/buzz#6961)674c173ebgive each channel thread its own agent session (feat(buzz-acp): give each channel thread its own agent session block/buzz#6732)0dbd036f5harness-agnostic effort write path (feat(agents): harness-agnostic effort write path and spawn bridge block/buzz#4625)47d068e21buzz gifs+ NIP-30 emoji tags (feat(cli): add buzz gifs command group and NIP-30 emoji tags on messages block/buzz#7259)Conflicts — five, all resolved to keep both sides
buzz-acp/src/queue.rsSessionScope). OurDropCountsis orthogonal, so it is preserved and re-expressed against scope keys. Also restores thepending_channelsdoc comment our earlier change displaced.buzz-acp/src/pool.rsbuild_messagegainedemoji_tags(NIP-30).post_noticekeeps its mention list, adds the new arg.buzz-acp/src/lib.rsOtherSetupAgentMarker.tsxUserProfileAgentManagementRows.tsxThe author-gate conflict
Upstream extracted the inbound author gate into
authorize_normal_listener_event/InboundAuthorGate. Their version improves attribution (effective_prompt_authorresolves delegated workflow authorship) but logs a refusal atdebugand posts nothing — taking it wholesale would have reverted #2 and made refused requests silent again.The resolution adopts upstream's structure and author resolution, and restores the one-notice-per-
(channel, author)reply plus owner mention around it, capturing the notice inputs before the gate consumes the event.Kind collision check
kind.rsauto-merged cleanly, which is the dangerous case for event kinds, so it was checked by hand. Upstream's only new kind isKIND_HUDDLE_LIVENESS(48104) — clear of our ticket kinds (30623, 30624).Verification
cargo check -p buzz-acp -p buzz-core— cleancargo test -p buzz-acp -p buzz-core— 885 passed / 29 failedupstream/mainin a separate worktree — 874 passed / 29 failed, and the two 29-name failure sets are identical. No regressions introduced; those tests are timing-sensitive and fail under load on this machine.tsc --noEmit— cleanPRODUCT_NAME = "Dreamforge",productName: "Dreamforge", 214 Dreamforge strings. RemainingBuzzoccurrences in renamed files are identifiers and comments, whichbrand.tsdeliberately excludes.🤖 Generated with Claude Code