mirror of
https://github.com/block/buzz.git
synced 2026-10-29 13:31:44 +00:00
cc-experiment
575 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8f3566ad9e |
Deduplicate ACP thread prompt context (#6706)
## Why This is a small isolated follow-up to [#6503](https://github.com/block/buzz/pull/6503): remove two proven duplicate prompt fragments without adopting its broader framing experiment or changing Buzz-native routing and identity behavior. ## What - Remove parsed `parent=<id>` only when it exactly duplicates parsed `root=<id>`; retain `root=<id>`, nested parents where parent differs from root, raw event tags, event IDs, author identity, mention fields and rules, and the explicit `--reply-to` instruction. - Remove the full-history retrieval hint only when complete, untruncated conversation or thread context is included. Thread context must include its root and cover every event rendered in the batch; mixed-thread batches and root-missing windows retain retrieval guidance. Context that is absent, previously delivered, or truncated also retains retrieval guidance, and the context header, count, and messages remain unchanged. ## Risk Assessment Low — the change is confined to Buzz ACP prompt rendering and preserves all routing, identity, event, and retrieval information outside the two exact duplicate cases. ## Testing - `cargo test -p buzz-acp` — 804 unit tests and 9 lifecycle integration tests passed on the clean rerun - Focused mixed-thread, same-thread, and missing-root prompt regressions passed - The timing-sensitive `keepalive_resets_idle_past_deadline` test passed in isolation after one unrelated full-suite flake, then passed in the clean full rerun - `cargo clippy -p buzz-acp --all-targets -- -D warnings` - `cargo fmt --all -- --check` - `just file-size-check` - Repository pre-push gate at `1b88793574e74e22f00a1fd19bfe9aa8e86150fa` — file-size, branch, Rust unit-test, and Desktop Tauri lanes passed - `git diff --check` Generated with Codex Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
de188ebf06 |
feat(projects): add agent and CLI project-home support (#6590)
## Summary - inject bounded project-home identity and repository context into managed agent sessions - add project-aware CLI flows for creating projects, repositories, issues, and related channels - match project homes through the existing relay query surface, then filter channel metadata client-side This is Part 1 of the channel-first Projects stack. Part 2 contains project creation and model foundations. ## Testing - `cargo fmt --all -- --check` - `cargo clippy -p buzz-cli -p buzz-acp --all-targets -- -D warnings` - `cargo test -p buzz-cli -p buzz-acp` — 1,184 tests passed, 1 doc test ignored - full pre-push gate passed ## Post-Deploy Monitoring & Validation - validate project-home agent context and project-aware CLI commands against a staging relay - healthy signals: project context matches the active channel, explicit repo coordinates remain stable, and normal channels receive no project block - failure signals: cross-channel project context, duplicate project creation, or commands targeting an unrelated repository; mitigate by reverting this PR --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz> Co-authored-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz> |
||
|
|
f177f4909d |
Use paired tags for standing & per-turn context (#6701)
## Why PR #6503 bundled a broader context compiler with several prompt-shape and telemetry changes, making it difficult to isolate whether paired XML-like top-level boundaries improve agent behavior. This keeps the follow-up experiment focused on framing only. ## What - Replace top-level standing and per-turn bracket headers with paired tags while preserving inner section content and event/message framing - Carry only metadata already present in dynamic headers as attributes, such as thread counts, truncation, event type, and batch count - Preserve every section body verbatim so the model executes the exact bytes reviewed in Desktop - Keep routing metadata, event content, batching, and delivery behavior unchanged - Teach Desktop diagnostics to parse the new framing while retaining compatibility with archived bracket-framed sessions; ambiguous paired boundaries render as the complete captured prompt ## Risk Assessment Medium — the change is limited to agent prompt framing and diagnostic parsing, but it affects both standing context and every dynamic agent turn. Bodies and delivery behavior remain unchanged, and compatibility parsing remains for prior sessions. ## References - Follow-up to #6503, scoped only to the paired-tag framing hypothesis ## Update — 2026-08-24 - Addressed review feedback by removing entity encoding/decoding from the execution and diagnostic paths. - Added regressions for literal closing tags, placeholders, entity-looking text, ampersands, and authored boundary whitespace. - Verified `cargo test -p buzz-acp` (805 unit + 9 integration), Desktop tests (5,412 before the main sync; 5,439 in the pre-push gate), Desktop typechecking, and Tauri clippy/tests. - Live local relay startup and message ingestion passed. The live ACP reply step is blocked locally because installed Goose 1.0.23 no longer provides the documented `goose acp` command and no alternate ACP adapter is installed. --- **Update Aug 25, 12:13 EDT:** Extended paired tags to per-turn top-level sections. - Converted context, conversation/thread context, event batches, and steer/interrupt boundaries without changing their inner bodies. - Preserved existing header metadata as attributes only; added no new prompt metadata. - Verified 806 Buzz ACP unit tests plus 9 integration tests, 5,441 Desktop tests, Desktop typechecking and formatting, and all differential pre-push lanes. --- **Update Aug 25, 13:58 EDT:** Addressed framing review feedback. - Updated operational base instructions to reference the emitted `<context>` section and added a regression guard against stale `[Context]` instructions. - Restored batched steer/interrupt cardinality in Desktop diagnostic titles with focused coverage for both paths. - Verified 807 Buzz ACP unit tests plus 9 integration tests, 5,442 Desktop tests, Desktop typechecking and formatting, and every differential pre-push lane including Tauri clippy/tests. --- **Update Aug 25, 15:37 EDT:** Hardened legacy transcript compatibility. - Semantic standing-context parsing now requires paired framing at the start and complete input consumption. - Historical bracket-framed personas keep literal balanced tag examples in their original section; malformed or mixed current framing displays as the complete captured prompt. - Added regressions for the reported legacy persona case and trailing unframed text; verified 60 focused parser tests, 5,444 full Desktop tests, formatting, and typechecking. --- **Update Aug 25, 16:31 EDT:** Preserved slash-command prompt block boundaries. - Desktop now sets aside a valid connector-facing slash-command block only when the following block begins recognized paired framing, parses the semantic blocks normally, and retains the command as visible Prompt metadata. - Added an end-to-end transcript regression using the production three-block ACP shape, including the user bubble, author pubkey, event ID, and grouped prompt context. - Verified 113 combined transcript tests, 5,445 full Desktop tests, formatting, typechecking, and all applicable pre-push lanes. Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
583af02299 |
fix(cli): preserve signatures in event reads (#6884)
## Summary
- preserve string Nostr `sig` values in normal `messages get`, `messages
thread`, `messages search`, and `feed get` output
- omit absent or non-string signatures instead of fabricating empty
values
- keep compact output at exactly `{id, content, created_at}` and update
CLI/managed-agent contract docs
## Validation
- `cargo test -p buzz-cli --lib` (367 passed at final head
`a2498faa0e07f90efc0ab0165606ba3b048327c7`)
- release candidate live-relay matrix: 49/49 normal events independently
recomputed to their NIP-01 IDs and passed BIP-340 verification; all four
compact commands preserved exact three-key output and ordering
- `git diff origin/main...HEAD --check`
- independent adversarial review: clear, no findings
## Compatibility
The normal JSON change is additive for valid relay events. In-repo
consumers use tolerant field lookup. Compact output is unchanged and
locked by regressions for both message and feed formatting.
Generated by Carl, an automated contributor working with Wes.
---------
Signed-off-by: Carl <9d00794d3df50972eb8b615511783cab12a77a8fd5dd5edd58073ec73b54bd8b@buzz.block.builderlab.xyz>
Co-authored-by: Carl <9d00794d3df50972eb8b615511783cab12a77a8fd5dd5edd58073ec73b54bd8b@buzz.block.builderlab.xyz>
|
||
|
|
ef0d202568 |
refactor(db): finish replaceable event store extraction (#6777)
## Why Finish the replaceable-event slice of [tracker #2](https://github.com/TheSentinel454/buzz/issues/2) and [domain issue #6](https://github.com/TheSentinel454/buzz/issues/6) without disturbing the runtime/store boundary established by #6660 and #6668. PR #6700 has merged; this PR now targets current main containing its replacement lock and transaction observability. ## What - Move `Db::replace_addressable_event`, its SQL, lock/transaction instrumentation, and focused addressable/parameterized replacement tests from `lib.rs` to `replaceable.rs` - Preserve the transaction-required parameterized API, replacement ordering, rollback semantics, mention indexing, and exactly one datastore span per public operation - Keep implementation and focused PostgreSQL tests co-located; follow-up review removed the dedicated replaceable ownership source guard as low value ## Stack - Exact base: main at |
||
|
|
8471049c43 |
feat(desktop): add KLIPY GIF search to composers (#5554)
## Summary - Supersedes #1913 with a KLIPY-hosted URL implementation. - Adds KLIPY GIF search and trending results to desktop message and forum composers. - Keeps selected GIFs hosted by KLIPY; Buzz stores only the external URL and media metadata (no imeta tag, since relays only accept hash-backed local `/media/` entries). - Aligns the Emoji/GIF picker with Buzz's standard segmented control, theme surfaces, and motion behavior. ## Relay-to-provider boundary - The relay proxies KLIPY search/share so the `BUZZ_KLIPY_API_KEY` never reaches the desktop; the key stays server-side behind a redacted `Debug` impl. - The dedicated GIF `reqwest` client sets `redirect::Policy::none()`. Because the API key rides in the request path, following a provider `3xx` could replay a key-bearing URL to an attacker-chosen host (an SSRF/key-disclosure primitive). With redirects disabled, a `3xx` returns as a non-success status that the handlers map to a generic `502`; the `Location` target is never read or forwarded. - Admission reuses the established NIP-98, membership, replay, and per-pubkey rate-limit gates, with an upstream response-size cap and allowlisting so KLIPY error bodies never cross the relay boundary. ## Accessibility - Under `prefers-reduced-motion: reduce`, the picker grid renders a static provider poster (a normalized `jpg` asset) instead of the animated preview, or a named static placeholder when no poster is available. It reacts to preference changes while mounted. `no-preference` keeps the animated preview. - Selected GIFs carry their title through `ImetaMedia`'s `displayLabel`, so the composer thumbnail, preview dialog, editor, lightbox, and remove control all derive one non-empty accessible name instead of an empty `Attachment ` label. Ordinary hashed uploads keep their existing hash-derived names. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> 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: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> |
||
|
|
ee6ca5fa28 |
Remove public relay signing key fallback (#6729)
Require `BUZZ_RELAY_PRIVATE_KEY` on every relay startup and remove the shared public fallback key completely. For local development, `just bootstrap` now generates a random key once in the gitignored `.env` file. Re-running bootstrap preserves that key, so relay-authored events retain the same identity across restarts. The local relay recipes reload `.env`, while isolated CI and E2E launchers pass explicit per-run keys. Startup fails immediately when the key is missing or invalid, before connecting to Postgres or Redis. Deployed relays continue to receive their stable key from the existing chart-managed secret. Validated with: - `./scripts/test-ensure-local-relay-key.sh` - `cargo test -p buzz-relay --bin buzz-relay` - `cargo clippy -p buzz-relay --bin buzz-relay -- -D warnings` - `cargo fmt --all -- --check` - `shellcheck scripts/ensure-local-relay-key.sh scripts/test-ensure-local-relay-key.sh` Signed-off-by: Jordan Mecom <jm@squareup.com> |
||
|
|
f249710331 |
Qualify canonical relay images for staged delivery (#6781)
## Why Phase 1 of progressive delivery needs an explicit eligibility boundary before deployment automation can safely consume relay artifacts. Today the canonical Docker workflow can publish independently of the same-SHA CI result, the relay does not expose its source/build identity, and the Helm chart only renders mutable tags. The existing `ghcr.io/block/buzz-staging-dev` workflow in #6709 remains a manual pre-merge preview lane. It is deliberately not a canonical promotion input. ## What changed - Compile the full source SHA and GitHub Actions build identity into canonical relay binaries and expose it at `/_status`. - Gate creation of tagged multi-architecture manifests on the latest exact-SHA `CI` push run succeeding on `main` or `release`. - Preserve the signed SLSA provenance and add a signed, release-only deployment-eligibility predicate with source, build run, qualifying CI run, and exact compatible Buzz chart version. - Keep failed or pending builds untagged and without an eligibility predicate; preview-package artifacts are excluded by package and signer contract. - Add optional `image.digest` support to Buzz chart `0.1.8`, retaining the existing tag fallback. - Document verification and the intentional boundary between image/chart compatibility and backwards-compatible database migrations. ## Risk and rollout Canonical tag publication now waits for same-SHA CI and fails closed when the newest matching run fails. Per-architecture blobs may remain untagged after a failed qualification; they are not promotion inputs. Debug manifests are also CI-gated but do not receive the release eligibility predicate. There are no migration or schema-orchestration changes. The companion BuilderBot infrastructure PR will pin a separately verified canonical main digest and expose a read-only running-deployment inspection command. Signed eligibility is available only for artifacts published after this workflow lands. ## Verification - Blox: workflow contract fixtures, including stale-green rejection and predicate structure - Blox: `actionlint` for `ci.yml` and `docker.yml` - Blox: 46 Helm unit tests, chart lint, and production/quickstart render fixtures - Blox: relay status unit test, workspace Rust formatting, and workspace all-target clippy - Live evidence: exact-SHA CI selector chose successful run `32857345153` for source `9d1e4b257657f382d3111ce748f3da8d063b7671` - Existing canonical digest provenance verified with `gh attestation verify` ## References - #6709 — developer-preview precursor; intentionally separate from canonical eligibility - `docs/deployment-identity.md` *Generated with Codex* --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> |
||
|
|
113a33b7e4 |
Add database pressure observability (#6700)
## Why Database pressure currently collapses several distinct delays into one symptom. This adds the evidence layer needed to distinguish pool acquisition wait, logical database operation time, advisory-lock wait, and selected transaction duration before changing timeout or retry policy. This is the phase 2 Lane A observability bundle for [#26](https://github.com/TheSentinel454/buzz/issues/26), [#28](https://github.com/TheSentinel454/buzz/issues/28), and [#33](https://github.com/TheSentinel454/buzz/issues/33). It is stacked on #6668. ## What - Record explicit reader/writer checkout wait and acquisition outcomes with `buzz_db_pool_acquire_wait_seconds` and `buzz_db_pool_acquisitions_total`. - Extend the compile-time `#[datastore_span(name = "...")]` seam with `buzz_db_operation_duration_seconds`, so operation labels remain static source literals instead of request data. - Route correctness-critical replacement, membership, push-gate, deletion, and migration/schema-safety advisory locks through one observer without changing their SQL, order, scope, or blocking behavior. - Measure six internally owned transaction lifetimes with `buzz_db_transaction_duration_seconds`, starting after `BEGIN` succeeds and ending after explicit commit/rollback or scope exit. - Emit root slow-operation warnings at 500 ms, logging the first slow completion and then 1/100 per call site with only `operation`, `outcome`, and `elapsed_ms`. - Document names, units, fixed label vocabularies, measurement boundaries, and blind spots in this PR description. Fixed labels are deliberately small: - `pool_role`: `writer`, `reader` - `lock_type`: `replacement`, `membership`, `push_gate`, `deletion`, `migration_schema_safety` - `outcome`: `success`, `error`, `timeout` where SQLx/PostgreSQL can distinguish it accurately - `operation`: compile-time datastore names plus the six closed transaction operation names documented in the runbook No metric or slow warning contains community IDs, event IDs, event kinds, coordinates, d-tags, SQL/query text, query IDs, returned errors, or event content. ## Coverage boundaries - Operation duration is the complete annotated logical function body, not pure SQL execution; it may include implicit checkout, lock wait, nested operations, and application work. Cancelled futures do not reach its completion hook. - Pool timing covers explicit helper checkouts, including proved-reader routing and selected writer-owned transactions. Implicit SQLx checkout through `&PgPool` remains folded into operation duration. - Lock timing covers application-side blocking locks in the five named families. Trigger/stored-procedure locks, channel-TTL locking, the usage try-lock, and the audit service session lock remain outside this slice. - Transaction timing covers only the six wholly owned boundaries documented in the runbook. It excludes pool wait, `BEGIN`, asynchronous rollback cleanup after an early return, and caller-owned `Db::begin_transaction` lifetime. ## Relationship to #6229 #6229 is the incident-driven timeout precursor. This PR does not add or change `statement_timeout`, `lock_timeout`, `idle_in_transaction_session_timeout`, retries, audit durability, or client-visible conflicts. It provides the missing distributions needed to evaluate those policies later and intentionally leaves #6229's open audit retry/durability finding untouched. The branches overlap in `crates/buzz-db/src/lib.rs` and `crates/buzz-db/src/migration.rs`, so a later rebase may need textual conflict resolution, but the behavior is complementary rather than duplicated. ## Risk assessment Moderate-low. The primary risk is instrumentation overhead and added static series. Cardinality is source-bounded, slow logs are sampled/redacted root events, and the lock/transaction changes wrap existing awaits without changing policy or ordering. ## Verification Author workstation: `buzz-tornquist-db-pressure-observability` (`2010927`), exact head `d7cf833e26c528adfcde3917ded80daf6f4ddac9`, parent `6f50e6b2b2a996349149af61d35bdd6a355f77fd`. - `cargo fmt --all --check` — passed - `cargo clippy -p buzz-datastore-tracing -p buzz-db -p buzz-audit -p buzz-search -p buzz-relay --all-targets -- -D warnings` — passed - `cargo test -p buzz-datastore-tracing --quiet` — 4 passed - `cargo test -p buzz-db --quiet` — 109 passed, 200 ignored - `cargo test -p buzz-audit -p buzz-search --quiet` — 16 passed, 25 ignored - `cargo test -p buzz-relay --lib --quiet -- --test-threads=1` — 906 passed, 48 ignored - Native PostgreSQL focused tests for pool success/timeout/error, lock success/contention/timeout/error, replacement, membership serialization, push ordering, deletion fencing, migration/schema exclusion, and reader fallback — 8 passed The default-parallel relay run passed once; subsequent runs exposed the existing load-sensitive `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo` 504 at the end of the suite. That test passes in isolation and the full relay suite passes serially. Independent exact-head review workstation: `buzz-tornquist-db-pressure-observability-review` (`2013067`). Formatting, the same all-target clippy command, datastore instrumentation tests, DB unit tests, source privacy guards, and diff/non-goal audits passed; no review findings. Generated with Codex --------- Signed-off-by: tornquist <tornquist@squareup.com> |
||
|
|
d12dea4e67 | Support community deletion in versioned media buckets (#6738) | ||
|
|
a1219070fa |
feat(buzz-agent): gate LLM tool calls on session/request_permission (#5712)
## Summary
buzz-agent now authorizes every LLM-issued MCP tool call through
`session/request_permission` before it executes, instead of running
tools unconditionally. The agent **always asks**; the client applies
`BUZZ_ACP_PERMISSION_POLICY` and answers. buzz-agent never reads the
policy — this keeps the policy decision on the client side, matching the
layering of the other ACP harnesses, and avoids duplicating policy logic
that would drift.
### Broker
A crate-local `PermissionBroker`
(`crates/buzz-agent/src/permission.rs`), owned by `App` for the
connection lifetime, owns the full request-correlation lifecycle:
- **Process-wide admission.** A global `Semaphore`
(`BUZZ_AGENT_MAX_PENDING_PERMISSIONS`, default 32, validated `>= 1`) is
acquired *before* any correlation entry is inserted. This is the
security bound: the per-turn tool semaphore is constructed fresh per
turn and `max_sessions` is unbounded by default, so neither bounds
simultaneously-outstanding asks process-wide.
- **Abort-safe cleanup.** A successful admission returns a
`PendingPermission` lease that owns the admission permit and the
correlation id. Its `Drop` synchronously removes the still-pending entry
and releases the slot, covering task abort/panic that bypasses the
normal `run_prompt` tail.
- **At-most-once resolution.** `deliver` claims (removes) the entry
*before* waking the waiter, so each id resolves once and a later lease
`Drop` is a no-op. Unknown/late ids are logged and dropped; only ids the
broker minted (`perm-<n>`) are recognized.
- **Single absolute deadline.** Admission, request enqueue, and response
wait all share one deadline (`BUZZ_AGENT_PERMISSION_TIMEOUT_SECS`,
default 330s, validated `>= 1`), so a saturated call cannot outlive one
timeout window even when a stalled writer blocks the enqueue.
Cancellation races inside every wait — resolution never depends on the
outer abort drain. A writer that dies mid-connection (stdout closed, or
a blocking write that only surfaces its error at flush) is
connection-fatal: it cancels all sessions, which resolves any ask
waiting on a reply that can never be written.
### Wire
`request_permission_params` (`crates/buzz-agent/src/wire.rs`) is
version-aware, keyed on the protocol version negotiated at `initialize`
and stored on `App` for the connection lifetime (never derived from a
later mutable session field). v2 nests the tool call under `subject:
{type: "tool_call", toolCall}` with top-level `title`/`options`; v1 uses
the legacy top-level `toolCall`. No hybrid shape. Both offered options
(`allow_once`, `reject_once`) carry `optionId == kind`, so the client's
`kind`-based selection and this side's `optionId`-based predicate agree
without a lookup table.
### Gate
In each spawned tool task (`crates/buzz-agent/src/agent.rs`) the
sequence is: acquire per-turn permit → argument-shape validation →
broker admission + request + wait → cancellation recheck →
`emit_in_progress` → `mcp.call`. Argument-shape validation is hoisted
out of `mcp.rs::do_call` into `validate_arg_shape` so a malformed
non-object argument is rejected locally without prompting for a call
that could never execute.
Authorization is fail-closed, stated once in `evaluate`: execute IFF
`outcome.outcome == "selected"` **and** the selected `optionId` equals
the offered allow option. Every other shape (reject, cancelled, JSON-RPC
error, malformed, unknown outcome, wrong/unknown option, timeout,
wire-channel closure) denies with a synthetic tool error, and the turn
continues.
### Scope
Only LLM-issued MCP calls are gated. The built-in `load_skill` tool and
`call_hooks` lifecycle calls (`_Stop`, `_PostCompact`) are exempt — they
are not model-issued. `readOnlyHint` is never treated as a security
boundary.
First cut ships `allow_once`/`reject_once` only; session-scoped grants
are deliberately out of scope.
### Related issue
Part of #4938. This PR and
[#5106](https://github.com/block/buzz/pull/5106) jointly implement the
feature: #5106 is the client-side policy engine and permission cards;
this PR is buzz-agent's asking side (`session/request_permission`).
Neither closes #4938 alone.
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Hayt <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
|
||
|
|
9d1e4b2576 |
Extract community persistence (#6668)
## Why Community persistence is the next incremental `buzz-db` store extraction, keeping tenant lifecycle SQL, records, tests, and instrumentation out of the database runtime module without changing behavior. ## What - Move community records and the existing `impl Db` operations into `community.rs` while preserving crate-root re-exports. - Move focused PostgreSQL tests with the implementation and enforce single ownership for each method and datastore span. ## Risk Assessment Low — this is a structural move of the existing records, SQL, method bodies, and focused tests; database runtime concerns, schema, and behavior remain unchanged. ## References - Architecture guardrail: https://github.com/TheSentinel454/buzz/issues/34 - Incremental tracker: https://github.com/TheSentinel454/buzz/issues/2 - Primary task: https://github.com/TheSentinel454/buzz/issues/5 - Stacked on: https://github.com/block/buzz/pull/6660 Generated with Codex Signed-off-by: tornquist <tornquist@squareup.com> |
||
|
|
8d2d0ff5ad |
Centralize replaceable event persistence (#6660)
## Why Command persistence duplicated NIP-33 replacement SQL in the relay and obscured the boundary between database runtime concerns and domain-store behavior. This proving slice establishes that boundary inside the existing `buzz-db` crate. ## What - Centralize parameterized-replaceable coordinate locking, ordering, replacement, watermark, and mention-indexing behavior in `buzz-db` - Expose transaction-required replacement and ordinary-insertion seams while keeping transaction ownership and workflow conflict messages in the relay - Cover concurrency, same-second ties, stale writes, replay, caller rollback, nested rollback recovery, and mention-index atomicity with focused PostgreSQL tests ## Risk Assessment Medium — this changes core event persistence and command idempotency paths, while intentionally preserving NIP-33, NIP-RS, mesh, and workflow conflict semantics. ## References - Architecture guardrail: https://github.com/TheSentinel454/buzz/issues/34 - Proving slice: https://github.com/TheSentinel454/buzz/issues/3, https://github.com/TheSentinel454/buzz/issues/4, https://github.com/TheSentinel454/buzz/issues/6 Generated with Codex --------- Signed-off-by: tornquist <tornquist@squareup.com> |
||
|
|
2d280376ad |
perf(desktop): persist channel heads, collapse thread reads and reply sends (#6572)
## Summary
Lands the build-now items from the desktop latency plan
(#ui-performance-deep-dive) as one change. Every perceived-latency hot
path a user hits on launch, channel open, thread open, and reply send
drops one or more round trips.
**A1 — persisted channel heads (the big one).** Native WAL SQLite cache
(`desktop/src-tauri/src/channel_head_cache.rs`) keyed by `{pubkey,
relayUrl}` scope, 32 rows/scope LRU, 1 MiB per-row drop cap,
schema-version reset, corrupt-row tolerance, checkpointed on shutdown.
Three blocking-pool commands: `channel_head_cache_load` / `_store` /
`_clear`. On the renderer side, `CommunityQueryProvider` kicks off
hydration of up to 12 heads when it constructs the query client — the
app, splash and relay preconnect mount immediately; only
`useChannelMessagesQuery` awaits the seed (`channelHeadHydration`), then
consumes a one-shot hydrated gate so a hydrated channel pays **zero**
`get_channel_window` calls on mount and exactly **one** on the
post-subscription refresh, whose response replaces page zero wholesale.
That refresh fires whether live-subscription setup succeeds or fails,
and is sequenced behind hydration so it is always a distinct
authoritative fetch (see Review follow-ups). Bounds-only persisted heads
(zero rows) are not hydrated and take the cold loading path. The
timeline loading latch recognizes native-hydrated rows as restart-safe
so they paint immediately instead of holding a skeleton. The cache is a
paint accelerator only — the relay response is always authoritative.
Replaces the legacy localStorage `messageSnapshot.ts` (removed, -401
lines).
Kill switch: `VITE_BUZZ_CHANNEL_HEAD_CACHE=off` at build time or
`localStorage["buzz-channel-head-cache"] = "off"` at runtime. Cache is
cleared on community removal and scoped per identity, so a replaced
signer never sees the previous identity's rows.
**B1 — thread aux in one response.** Relay thread filters accept
`include_aux`; the bridge appends the same authorized two-hop
reactions/edits/deletions closure a channel window gets
(`build_aux_query` shared with the window path). Renderer
`useThreadReplies` drops its two follow-up aux fetches. `next_cursor` is
computed from reply-kind rows only since aux rows are unpaged.
Documented in `docs/bridge-channel-window.md`. Thread queries keep
`staleTime: 0` (`bcfe04e2f`): an earlier revision raised it to 30s,
which CI's `thread-unread.spec.ts` caught — once the user leaves a
channel, the live subscription stops feeding that thread's cache, so a
reopen must always take the (now single) authoritative read.
**B2 — cached root on reply send.** `send_channel_message` gains
`root_event_id`; when the renderer already holds the parent (channel or
thread cache) it passes the NIP-10 root, and native signs without the
relay round trip that `resolve_thread_ref` used to make. Strict hex
parse; `root_event_id` requires `parent_event_id`; absent root falls
back to the existing relay resolution. The renderer never sends a
guessed root.
**B4** general HTTP pool idle 10s→300s, max idle per host 1→2. **B5**
relay preconnect fires as soon as identity is ready instead of waiting
for `requestIdleCallback`. One e2e test (`relay-reconnect.spec.ts`
"service restart close resets accumulated backoff") had been relying on
the idle-callback batching to skip past its own seeded dial failures
before the channel list painted; `8133d70bb` makes it wait for the
connected state instead (test-only, still fails with the 1012 backoff
reset disabled). **B6** profile freshness 60s→10 min (both the in-memory
entry check and the query `staleTime`). Tradeoff: another user's
display-name/avatar edit can take up to 10 min to propagate to a client
that already holds their profile (relay reconnect refetches
`users-batch` but resolves from the still-fresh per-pubkey entry); your
own edits still evict the entry immediately (`evictUsersBatchEntries` in
`useUpdateProfileMutation`).
### Related issue
Follows #6456/#6457/#6459/#6460 (already merged). #6455 is the
measurement instrument and is intentionally not folded in. No duplicate
PR found.
### Review follow-ups
Addressing Carl's reviews
[5001114109](https://github.com/block/buzz/pull/6572#pullrequestreview-5001114109)
and
[5002596542](https://github.com/block/buzz/pull/6572#pullrequestreview-5002596542),
each pushed as new commits (no rebase):
- `4f06b7770` fix(desktop): mount app while channel heads hydrate;
always revalidate — provider no longer gates children on the cache load;
`refreshAfterSubscribe` runs on subscribe failure too; bounds-only heads
skipped at seed; seed merges into an existing window store. +3 tests.
- `35834cb31` fix(relay): drain aux closure hops across the page clamp —
`query_all_pages` walks the `(created_at, id)` keyset via
`until`/`before_id` until a short page (`AUX_PAGE_LIMIT` =
`DEFAULT_MAX_PAGE_LIMIT`, `AUX_MAX_PAGES` = 64 warn+truncate) so
one-shot `limit: 1000` newest-first no longer drops the oldest
edits/deletions. +3 tests; `docs/bridge-channel-window.md` updated.
- `db21b0531` merge of `origin/main` `e23632941` (#6558, #6312 — no
overlap).
- `5a5566c0f` fix(desktop): sequence post-subscribe refresh behind
channel head hydration — `refreshChannelWindowMessages` awaits
`channelHeadHydration()` and, for a hydration-seeded query (`data !==
undefined && dataUpdatedAt === 0`), the in-flight snapshot fetch before
invalidating. Without this, a subscription that settles before the
SQLite load invalidated a data-less in-flight query; TanStack dedupes
that onto the existing fetch (`query-core` `fetch()` only cancels when
`state.data` exists), which returned the seeded snapshot — 0
authoritative fetches. Regression test reproduces Carl's exact ordering
(fails at `35834cb31` with 0 calls), plus a cold-channel guard that the
fix does not double-fetch.
- `b129231c8` fix(desktop): let concurrent post-hydration refreshes
share one window fetch — found independently by Max and Wren reviewing
`5a5566c0f`: subscribe settlement + reconnect both wake on the same
snapshot promise and both invalidate; the second (default
`cancelRefetch: true`) cancelled and replaced the first authoritative
fetch (3 queryFn calls, not 2, and the cancelled Tauri invoke still hits
the relay). The seeded branch now invalidates with `cancelRefetch:
false` so a second waker joins the in-flight fetch; cold/warm keep the
default (`test_canceled_stale_fetch_cannot_overwrite_catch_up_window`
relies on it). Concurrent regression test fails at `5a5566c0f` with 3.
### Testing
At `b129231c8` (PR head; verified in one shell with `git rev-parse HEAD`
= `b129231c8`): `pnpm check`, `tsc --noEmit`, desktop unit 5,393 / 0,
Playwright `boot-splash` + `channel-head-restart` + `relay-reconnect` +
`relay-reconnect-affordance` + `thread-unread` 34 / 34 on a fresh
`build:e2e`, pre-push hooks green.
At `5a5566c0f`: `pnpm check`, `tsc --noEmit`, desktop unit 5,392 / 0,
Playwright `boot-splash` + `channel-head-restart` + `relay-reconnect` +
`relay-reconnect-affordance` + `thread-unread` 34 / 34 on a fresh
`build:e2e`, pre-push hooks green.
At `35834cb31`: desktop unit 5,390 / 0; `cargo test -p buzz-relay --lib`
910 / 0; fmt + clippy `-D warnings` clean; Playwright 32 / 32 (same
specs minus affordance); GitHub CI green on every job except Smoke (3)
(unrelated project-review row-count + messaging timing flake, per Carl)
and Unit Tests (sherpa cache skeleton, below).
Earlier, all at `8133d70bb` (this PR head is `0c492366d` =
|
||
|
|
f99532585a |
refactor(acp): clarify agent prompt sections (#6501)
## Why The ACP prompt puts a machine-specific Workspace prefix before static base guidance and labels the user-facing agent instruction layer as the generic System section. Because the cwd varies by launch and worktree, leading with it reduces reusable prompt-prefix stability. `[Workspace]` was added in [PR #1194](https://github.com/block/buzz/pull/1194) as a defensive fix after a broken `~/.sprout` → `~/.buzz` migration caused agents to scan `$HOME` and trigger macOS TCC prompts. This change retains that grounding while shrinking it to the current working directory and moving dynamic environment context after the static Base prompt. ## What - Emit the prompt in Base → Workspace → Agent Instructions order - Reduce Workspace to `Current working directory: <absolute path>` - Resolve cwd as an absolute native-platform path and preserve Windows drive/UNC paths instead of checking for a leading `/` - Emit Agent Instructions for persona and standalone agent instructions across modern and legacy ACP paths - Preserve parsing for archived observer frames that used System or the former Workspace-before-Base order, and align the persona catalog label ## Risk Assessment Medium-low — this changes prompt framing for every newly created agent session. Existing archived observer frames remain parseable, and execution still uses the same ACP working directory. Cwd resolution now fails clearly instead of substituting `/` when the process directory cannot be resolved. ## References - https://github.com/block/buzz/pull/1103 - https://github.com/block/buzz/pull/1194 Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
8c0f42e187 |
Add mobile Huddles voice MVP (#6056)
## Summary - add foreground mobile Huddles on Android and iOS with native Opus capture/playback, mute, speaker routing, participants, lifecycle, and minimized drawer UI - keep mobile Huddle cards and roster state live, including ended rooms, relay-resolved profiles, and agents - broadcast desktop agent TTS through the existing Huddle audio protocol ## Scope Foreground human-to-human voice MVP only. Agent setup/transcripts, background calling, recording, and advanced device controls remain out of scope. ## Validation - `just mobile-check` - `just mobile-test` — 1,500 passed - `just desktop-check` and `just desktop-test` — 4,957 passed - desktop typecheck, strict Clippy, and Tauri tests — 2,445 passed, 15 ignored - mobile worktree identity contract checks - physical Pixel/iPhone behavior reviewed during development --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: leader <71e9f2c44a6932b6772caaaccda1911d010463c3e2c6c40410b8329956046801@buzz.block.builderlab.xyz> Co-authored-by: Tom Brow <tomb@block.xyz> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz> |
||
|
|
47526784d5 |
fix(deletion): allow IRSA S3 credentials (#6495)
## Why `buzz-admin deletions` runs inside bb-public relay pods, where S3 credentials are provided by the pod credential chain rather than static `BUZZ_S3_ACCESS_KEY` / `BUZZ_S3_SECRET_KEY` values. The deletion CLI was requiring those two env vars to be non-empty before constructing the shared media storage client, so it could not reach the existing default AWS credential chain. ## What - Treat missing/blank deletion S3 access key and secret key as an empty credential pair so `MediaStorage::new` can use `Credentials::default()`. - Keep the existing static-credential path unchanged when both values are non-empty. - Keep deletion unit tests deterministic by covering only deletion env normalization for missing/blank pair, trimmed static pair, and partial/whitespace-partial outputs; shared media tests continue to own credential-pair enforcement. ## Risk Assessment Low and scoped to the operator-only community deletion CLI startup path. The shared media storage credential validation still owns static-vs-default credential selection and still rejects mixed partial credentials. ## Testing At committed head `0a86c2914b1f97caf4788a771048aa8d9d9d88ac` with a clean worktree before and after (`git rev-parse HEAD` before/after matched): - `just fmt-check` — passed. - `cargo test -p buzz-deletion` — passed: 12 passed, 9 ignored. - `cargo test -p buzz-media` — passed: 120 passed; `static_creds_round_trip_against_minio` remained ignored because it requires live MinIO. - `cargo test -p buzz-admin` — passed: 1 passed. - `cargo clippy -p buzz-deletion --all-targets -- -D warnings` — passed. - Startup smoke at the same head: built `buzz-admin`, then ran `target/debug/buzz-admin deletions drain` with `BUZZ_S3_ACCESS_KEY=` and `BUZZ_S3_SECRET_KEY=' '` plus `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` fallback credentials; command exited `0`, proving startup transitions past deletion S3 key validation and exercises the shared default credential-chain branch using AWS env fallback credentials. - `git push origin HEAD:seiler/deletion-irsa-credentials` — passed; pre-push hooks passed. Not run: the full `TESTING.md` live-local relay workflow. Docker Desktop currently refuses CLI access on this machine with `Sign in to continue using Docker Desktop. Membership in the [squareup] organization is required.` ## References - Buzz channel: `buzz://message?channel=9e4aabc6-414c-4978-aba7-b9f5228776de&id=177c5b8ad9e78a647f438ec7040d25f0c20a7c2b8678dd0e34768effe053f7f4` Generated with Codex --------- Signed-off-by: coder 0 <d97ebdbb198c7237c94f84ea8bb8a73583ea067407eebd0062abbb3962527fb1@buzz.block.builderlab.xyz> Co-authored-by: coder 0 <d97ebdbb198c7237c94f84ea8bb8a73583ea067407eebd0062abbb3962527fb1@buzz.block.builderlab.xyz> |
||
|
|
025425591e |
fix(benchmarks): wait for scripted event delivery (#6487)
## Why The Buzz-native runtime could stop a solo agent after one turn even when a scripted follow-up event was queued or already running. That could turn a harness timing race into a benchmark failure. Follow-up to #6448. ## What - Treat solo tasks with scripted events separately from ordinary one-turn tasks. - Wait until turn counts and authored message IDs remain idle and unchanged for several polls before stopping the agent. - Remove the message-count shortcut that could stop an active turn. - Add focused regressions for a delayed follow-up turn and an already-running turn with a `DONE:` message. ## Testing - `pytest -q tests/test_container_runtime.py -k "solo_turn_end or scripted_events"` — 3 passed - `ruff check` on the three touched Python files - `ruff format --check` on the three touched Python files - Local `buzz-native-solo-luna.yaml` Harbor run, one attempt per affected task: - `cross-thread-requests` — reward 1.0; all 6 task-specific checks passed - `interleaved-agent-reports` — reward 1.0; all 7 task-specific checks passed - 2 completed trials, 0 exceptions, 56 seconds total ## Risk Low. This only changes completion detection for solo benchmark tasks with scripted events. Ordinary solo tasks retain immediate completion after their first turn ends. --- **Update Aug 21, 13:07 EDT:** Replaced the fixed settle delay with exact delivery receipts after [review feedback](https://github.com/block/buzz/pull/6487#discussion_r3832050452). - ACP now records the event IDs delivered by each completed turn; the runtime also recognizes existing successful-steer receipts. - Scripted trials stop only after every expected event ID is acknowledged and no turn is active. Missing receipts wait for the trial budget instead of producing partial evidence. - TDD regression delayed delivery beyond the old five-poll boundary and failed before the implementation; the receipt parser and Rust receipt format are pinned independently. - Focused verification: 31 container-runtime tests passed, 6 ACP delivery tests passed, plus Ruff, rustfmt, and Clippy. - Receipt-gated Luna run: 2 completed trials and 0 exceptions. `interleaved-agent-reports` scored 1.0. `cross-thread-requests` completed both calculations and thread isolation but Luna omitted the user mention on ALPHA, so that model-output dimension scored 0. - Risk remains low: the ACP production change adds delivery receipt logging and restores a missing turn-end log on a completed-before-control race; it does not change queue dispatch behavior. Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
24ec6a468e |
Repair stale large channel roster snapshots (#6251)
## Summary - detect relay-authored NIP-29 kind 39002 roster snapshots truncated by the former 1,000-member query cap and repair stale large rosters during relay startup - serialize canonical roster capture and replacement with membership writes, preserving tenant, channel, signer, pubkey, and role boundaries through mixed-version deployments - install migration 0032's fail-closed roster fence on the partitioned events table and verify its catalog shape plus behavior before opening relay listeners ## Rollout Migration 0032 is a hard schema-before-code compatibility boundary. Apply migrations before rolling this relay version. Startup refuses to open listeners when the parent/partition triggers are missing, disabled, mis-shaped, or behaviorally inert. For large installations, prefer `buzz-admin migrate` and monitor lock acquisition as documented in the chart README. ## Validation Exact head: `bcbba271f54bc0046a6683007e5a2b70403a11d5` - rebased onto `569308c23c9c2bf620dd3a9a5e4baecbcfa22e16`; the nine-file feature patch is byte-identical to pre-rebase head `be8ea0084f4d4c78c7c2550baad4399e4df8ce73` - pre-push hook passed at exact head: branch-skew, file-size, full Rust unit suite, Desktop Tauri clippy, and Desktop Tauri tests - `cargo fmt --all -- --check` - `cargo test -p buzz-relay group_members_snapshot_keeps_members_past_one_thousand -- --nocapture` - focused CI-mode Playwright regression: `selected relay agents revoked after the invite prompt cause no side effects` passed at exact head - prior exact-patch validation: `large_roster_reconciliation_candidates_respect_snapshot_count_and_signer`, mixed-writer locking/rollback, migration admission, partition trigger coverage, and desired-schema parity regressions ## Review Independent DB/relay review found no blocking issues in the exact feature patch. The concurrency fence holds the established replacement and membership locks on one transaction/connection through replacement; failures roll back both soft-delete and insert. Reconciliation remains tenant/channel/signer scoped and validates exact normalized pubkey-plus-role membership. The prior red Desktop shard was unrelated to this backend-only diff: its mocked mention test exercises no relay, database, or migration path. It reproduced as a timing flake on the old head, passed on retry/base, and now passes locally after rebasing onto current main. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> |
||
|
|
c6e3fe7dfa |
feat(workflows): reply in-thread from send_message action (#6178)
The `send_message` workflow action could only post new top-level channel
messages. This adds a `reply_in_thread` option so a `message_posted`
workflow can reply in the triggering message's thread, plus a
`trigger_is_reply` filter variable so a workflow can fire only on
top-level messages.
Every NIP-10 thread-marker reader in the tree now routes through one
shared parser and one collapse rule in `buzz-core`, deleting four
hand-rolled copies that had drifted on id-validity and marker semantics.
## What changed
- **Schema** (`crates/buzz-workflow/src/schema.rs`): new
`reply_in_thread: bool` (serde default `false`) on
`ActionDef::SendMessage`. `validate()` rejects `reply_in_thread: true`
on `schedule`/`webhook` triggers — they carry no message to reply to.
- **Executor** (`crates/buzz-workflow/src/executor.rs`): threads
`reply_to: Option<&str>` (the trigger `message_id`) through
`ActionSink::send_message` when `reply_in_thread` is set; errors clearly
if the trigger has no `message_id`. `resolve_step_templates` carries the
new field. Adds `trigger_is_reply` (boolean) to the eval context.
- **Relay sink** (`crates/buzz-relay/src/workflow_sink.rs`): when
`reply_to` is set, resolves parent/root/depth from the known trigger
event, persists real thread metadata instead of the hardcoded top-level
`depth: 0`, and pushes the live kind:39005 thread-summary overlay after
insert so subscribed desktops update the root's reply badge without
refetching — matching the human ingest path. Emits the same NIP-10
`e`-tag shape as `buzz_sdk::builders::thread_tags`: a single `["e", id,
"", "reply"]` tag for a direct reply (parent == root), and the `root` +
`reply` pair only when nested — so every writer produces one wire shape
per reply kind.
- **Ingest resolver** (`crates/buzz-relay/src/handlers/ingest.rs`): new
`resolve_relay_reply_thread_meta` + `ReplyAncestry`. Unlike the
client-facing `resolve_nip10_thread_meta` (which validates
client-supplied tags), this computes root/depth from the known parent
and enforces the same same-channel and depth-limit invariants. When the
parent has no `thread_metadata` row, both resolvers share
`derive_ancestry_from_parent_tags`, which applies
`ThreadMarkers::resolve()`: a marked nested parent remains depth 2,
while a root-only, malformed, or unmarked parent is top-level and starts
its reply thread at itself. Both the client resolver and the
parent-ancestor fallback now read markers via the shared parser instead
of hand-scanning `e`-tags.
- **Shared NIP-10 parser** (`crates/buzz-core/src/nip10.rs`):
`parse_thread_markers` reads an event's `root`/`reply` markers, honoring
a marker only when its event id is exactly 64 ASCII-hex characters.
`ThreadMarkers::resolve()` is the single definition of the (root, reply)
→ (root_id, parent_id) collapse: `root`+`reply` as-is, `reply`-only →
`(reply, reply)`, a lone `root` or neither → top-level. A slice-based
`parse_thread_markers_from_parts` entry point serves consumers holding
raw JSON tag arrays. Relay ingest, the workflow `trigger_is_reply`
predicate, ACP anchoring, and the CLI reply resolver all call these, so
they cannot drift on marker, id-validity, or collapse semantics.
- **ACP** (`crates/buzz-acp/src/queue.rs`): `parse_thread_tags`
delegates marker parsing and collapse to `buzz_core::nip10`, keeping
only its local `p`-tag mention collection. This fixes a parity gap with
ingest: a malformed non-64-hex marker id is no longer counted as a
thread link, and a lone `root` marker is now top-level rather than being
treated as root == parent.
- **CLI** (`crates/buzz-cli/src/commands/messages.rs`):
`find_root_from_tags` routes its JSON tags through the shared slice
parser and collapse rule. CLI replies to root-only, malformed, or
unmarked parents now correctly start at that immediate parent;
reply-only and root+reply parent ancestry remain intact.
- **Trigger context** (`crates/buzz-workflow/src/lib.rs`):
`build_trigger_context` derives `is_reply` solely from a valid NIP-10
`reply` marker (no DB hit). A lone `root` marker is top-level to ingest,
so it does not count; neither does a `reply` marker whose event id is
malformed, nor a bare/unmarked `e`-tag (mentions, quotes).
- **Desktop** (`workflowFormTypes.ts`, `WorkflowStepCard.tsx`):
`replyInThread` form field with YAML round-trip, and a "Reply to
triggering message in thread" checkbox shown only for message-based
triggers. Switching to `schedule` or `webhook` clears the option on
every step before serialization, so a value hidden behind an action
change cannot resurrect when the action is switched back. The form
parser also requires `reply_in_thread` to be a boolean and refuses
`reply_in_thread: true` under an ineligible trigger, falling back to
YAML mode rather than silently normalizing a backend-invalid definition.
## Usage
```yaml
trigger:
on: message_posted
filter: trigger_is_reply == false
steps:
- id: auto_reply
action: send_message
text: "pre-written reply text"
reply_in_thread: true
```
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
|
||
|
|
b728a2af31 |
feat(llm): stamp thinking effort on call-completed log line (#6424)
The performance-analysis pipeline measures harness × provider × model × effort from production logs, but thinking effort was the only dimension not stamped per call — it had to be attributed by config-changeover timestamp. Add `thinking_effort = ?cfg.thinking_effort` to the `llm: call completed` `tracing::info!` in `Llm::complete()` so every call self-describes all four dimensions. `?`-formatting preserves the `None`-vs-`Some(...)` distinction the same way the token fields keep None-vs-zero. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
84c095f8be |
feat(cli): accept Buzz message links for thread reads (#6359)
## Summary - let `buzz messages thread` accept a canonical `buzz://message` link directly - treat the selected message ID as authoritative, verify its channel, and derive its containing thread root from the event - reject stale or forged `thread` hints that disagree with the selected message - preserve the existing `--channel` / `--event` form while allowing it to target any message in a thread ## Why Agent skills should be able to hand first-class Buzz links to the public Buzz CLI instead of carrying their own URL parsing wrappers. Link handling belongs with the CLI that owns the protocol; identity and relay selection remain explicit through the existing Buzz CLI environment and do not read Buzz Desktop private state. ## Verification - `cargo test -p buzz-cli` — 360 passed - `cargo clippy -p buzz-cli -- -D warnings` - `cargo fmt --all -- --check` - repository pre-push gates, including 5,070 Desktop tests - final architecture/code review — no must-fix findings A relay-backed manual smoke test was not run because this session did not have `BUZZ_RELAY_URL` and `BUZZ_PRIVATE_KEY` configured; the change has focused parser, CLI argument, channel/root binding, and mismatch tests. --------- Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> |
||
|
|
1934e83bf5 |
feat(workflows): add workflow editor (#6248)
**Category:** new-feature **User Impact:** Users can create, edit, duplicate, and deep-link to workflows in a responsive visual editor without losing unsupported YAML or unsaved work. **Problem:** Workflow editing was split across disconnected surfaces and lacked reliable URL state, lifecycle protection, and parity between Form and YAML modes. **Solution:** This adds a route-addressable editor foundation with stable pane identity, guarded dirty exits, lossless Form/YAML transitions, responsive workflow and channel controls, and matching reaction-filter execution support. <details> <summary>File changes</summary> **crates/buzz-workflow/src/lib.rs** Apply reaction trigger filters during workflow execution and cover target-message gating. **crates/buzz-workflow/src/schema.rs** Extend the reaction trigger schema with the editor-owned filter field. **desktop/src/app/navigation/useAppNavigation.ts** Add navigation helpers for explicit workflow create, edit, and duplicate editor modes. **desktop/src/app/routes/WorkflowsRouteScreen.tsx** Coordinate route state with the shared workflow library and editor dialog. **desktop/src/app/routes/lazyWorkflowsRouteScreen.ts** Share one lazy route component across workflow route entry points to avoid loading flashes. **desktop/src/app/routes/workflows.$workflowId.tsx** Parse workflow editor modes and pane deep links for workflow-specific URLs. **desktop/src/app/routes/workflows.tsx** Parse library-level create state and render the shared workflow route screen. **desktop/src/app/AppWorkflowEditorOverlayProvider.tsx** Host the shared workflow editor at the app-shell level so channel-originated workflow dialogs stay above the active channel instead of replacing it. **desktop/src/shared/context/WorkflowEditorOverlayContext.tsx** Expose route-independent open-existing and create-new workflow actions to channel settings. **desktop/src/features/workflows/ui/WorkflowEditorHost.tsx** Share editor loading, unavailable, and dialog lifecycle wiring between canonical workflow routes and the channel overlay. **desktop/src/features/channels/ui/ChannelManagementSheet.tsx** Add an experiment-gated, Canvas-style Workflows ingress below Canvas, including channel-scoped loading, error, empty, and list states plus open/create actions; disabled users issue no workflow query. **desktop/src/features/channels/ui/ChannelWorkflowsSection.tsx** Render the channel workflow list and New workflow action without pushing the existing settings sheet past its file-size ceiling. **desktop/src/features/workflows/ui/ChannelCombobox.tsx** Adopt the final channel presentation, portalled scrolling, and one-shot create-flow opening behavior. **desktop/src/features/workflows/ui/CreateWorkflowDialog.tsx** Remove the superseded create-only dialog in favor of the unified workflow editor. **desktop/src/features/workflows/ui/WorkflowCard.tsx** Open workflow cards in the detail and run-history modal while preserving explicit edit and duplicate actions. **desktop/src/features/workflows/ui/WorkflowDetailDialog.tsx** Present workflow Trigger/Steps in the shared modal chrome, with top-chrome ingress to a responsive right-side run-history inspector and an explicit edit action. **desktop/src/features/workflows/ui/WorkflowDialog.tsx** Unify create, edit, and duplicate lifecycle handling with URL panes, generated-name synchronization, dirty-exit guards, stale-write preservation, and protected webhook-secret handoff. **desktop/src/features/workflows/ui/WorkflowFormBuilder.tsx** Build the responsive Form/YAML editor shell, stable step selection, insertion and removal behavior, and lossless canonical-YAML synchronization. **desktop/src/features/workflows/ui/WorkflowStepCard.tsx** Align workflow step controls and presentation with the final editor interaction model. **desktop/src/features/workflows/ui/WorkflowUnavailableDialog.tsx** Show a non-disclosing loading or unavailable state for missing and inaccessible workflow links, with retry and close actions. **desktop/src/features/workflows/ui/WorkflowWebhookSecretDialog.tsx** Obscure one-time webhook secrets by default and require explicit confirmation before any close or navigation discards them. **desktop/src/features/workflows/ui/WorkflowsScreen.tsx** Connect library state and workflow actions to the route-addressable editor. **desktop/src/features/workflows/ui/WorkflowsView.tsx** Restore the responsive workflow library, create tile, cards, loading states, and shared action menu. **desktop/src/features/workflows/ui/workflowEditorPane.test.mjs** Cover pane parsing, serialization, and stable step-ID reconciliation. **desktop/src/features/workflows/ui/workflowEditorPane.ts** Define explicit trigger and stable step pane URL state. **desktop/src/features/workflows/ui/workflowYamlDocument.ts** Read and update header fields independently of full form validation so incomplete steps cannot clear or disable the workflow title. **desktop/src/features/workflows/ui/workflowYamlDocument.test.mjs** Cover document-level workflow header reads and writes for incomplete definitions. **desktop/tests/e2e/workflow-title-stability.spec.ts** Verify generated, renamed, saved, and duplicated titles remain stable while moving between trigger and step panes. **desktop/src/features/workflows/ui/workflowFormTypes.test.mjs** Cover lossless Form/YAML round trips and actionable fallback for unsupported fields. **desktop/src/features/workflows/ui/workflowFormTypes.ts** Own canonical workflow YAML conversion while preserving supported trigger and step fields. **desktop/src/shared/ui/PortalledScrollArea.tsx** Provide bounded scrolling for popovers rendered outside their dialog container. **desktop/src/shared/ui/popover.tsx** Allow workflow popovers to use the shared portalled scroll container. **desktop/tests/e2e/channels.spec.ts** Cover disabled and enabled Workflows experiment states, including suppressed queries while disabled, placement beneath Canvas, channel workflow listing and opening, channel-preselected workflow creation, and direct return to the channel Workflows panel after close, discard, or cancel. **desktop/tests/e2e/workflows.spec.ts** Exercise library actions, deep links, create/edit/duplicate lifecycle, dirty exits, responsive editor behavior, YAML safety, stale updates, and one-shot channel selection. </details> ## Reproduction steps 1. Open **Workflows** and confirm the responsive card library, create tile, card action menu, and card-to-detail/run-history modal navigation. 2. Open `?view=create`; confirm the channel chooser opens once, the trigger inspector stays hidden until a channel is selected, and closing the chooser does not make it reopen after unrelated edits. 3. Create a workflow, switch between Form and YAML, add and remove steps, refresh a pane deep link, and confirm the selected trigger or stable step remains addressable. 4. Edit or duplicate a workflow, make an unsaved change, and confirm close, Escape, browser navigation, and route target changes require discard confirmation while pane-only navigation does not. 5. Enter unsupported YAML and confirm Form mode gives an actionable fallback without rewriting the definition; verify reaction triggers preserve and execute their filter. 6. Open a channel’s settings, select **Workflows** below **Canvas**, and open or create a workflow; confirm the shared modal stays over the channel, the channel URL does not change, New workflow preselects that channel, and closing or discarding returns directly to the channel’s **Workflows** panel. 7. In create, edit, and duplicate modes, move between the trigger and incomplete step panes and confirm the generated or edited title remains visible and editable. ## Screenshots Fresh captures from product head `c5c3abc91a71fe511d43e6cc9168b1626d0c217c`; the later review-guidance fix does not alter these pictured states. ### Workflow library and actions  ### Workflow editor — wide  ### Workflow editor — narrow inspector overlay  ### Workflow editor — active channel overlay  ## Review feedback addressed - Corrected Diff Posted condition guidance to use the executor-supported `str_contains(trigger_text, "deploy")` syntax and added a visible Playwright regression assertion. - Scoped direct workflow-detail navigation coverage to the stable `Edit workflow` dialog name, asserted the workflow title separately, and retained the trigger-node assertion. - Routed dirty channel-overlay Duplicate/Edit transitions through the existing discard confirmation, preserving the original YAML draft when the user keeps editing. - Made workflow deletion await relay success, remain non-dismissible while pending, and retain the confirmation/editor/draft with an actionable inline error on rejection. - Gated the channel-settings Workflows ingress and channel workflow query behind the `workflows` experiment, with defensive rendering if the flag changes while that view is active. - Matched the Reaction Added trigger to the prototype reaction picker, preserving native/custom/legacy values in canonical YAML and providing an explicit clear action. - Kept a channel’s Workflows panel mounted beneath channel-origin editors so clean close, dirty discard, and create cancel return directly to that panel without changing the channel URL. ## Verification Verified at exact pushed head `76ebba7b7ace1e13445744200da70ca9da231b7e`: - Push hooks passed: destination-org policy, branch-skew, differential file-size, Desktop checks, TypeScript typecheck, and **5,115/5,115** Desktop unit tests - Focused channel lifecycle E2E passed: dirty edit discard returns directly to the channel Workflows panel; create cancel does the same while preserving the channel URL and preselected channel - E2E production build, standalone TypeScript typecheck, and Biome checks on both touched files passed - Reaction-picker regression spec remains recorded at `dc28ffa98ec34b4e0656757fb2dd93e60a84674d`: **5/5 passed** (picker interaction, canonical YAML persistence/clear, legacy-value preservation, save/reopen round trip, narrow viewport containment) - Blox existing workflow E2E regression set at `dc28ffa98ec34b4e0656757fb2dd93e60a84674d`: **36/36 passed** - Earlier blocker and review regressions remain recorded at `3760c3d657a525f5af98e8d0f98bdd03999d8e61`: **5,114/5,114** Desktop unit tests and the dirty-overlay/deletion focused checks - Working tree clean; local branch, remote branch, and PR head all match the exact SHA above ### Related issue None found. Closest prior work: #231. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
2ce8df8533 |
fix(models): curate Databricks alias-aware labels for 5 missing endpoints (#6360)
Curates Databricks model labels through prefix-stripping aliasing and materializes exact records for five endpoints missing from the Databricks catalog. **Alias-aware label resolution** (Databricks v2) - Strip workspace-specific prefixes (`goose-`, `team-x-`, etc.) before exact-record lookup so any prefixed alias resolves to its canonical label without enumerating every alias variant. - Return `None` for uncurated ids instead of falling back to a raw string; callers control the fallback display. **New exact records — label-only (Anthropic Messages route, axes from family rules)** - `databricks-claude-fable-5` → "Claude Fable 5" (materialized from `anthropic-adaptive-xhigh-fable-5`) - `databricks-claude-opus-4-8` → "Claude Opus 4.8" (materialized from `anthropic-adaptive-xhigh-opus-4-8`) - `databricks-claude-opus-5` → "Claude Opus 5" (materialized from `anthropic-adaptive-xhigh-opus-5`) - `databricks-claude-sonnet-5` → "Claude Sonnet 5" (materialized from `anthropic-adaptive-xhigh-sonnet-5`) **New capability-bearing record — Kimi K3 (MLflow Chat route)** - `databricks-kimi-k3` → "Kimi K3" with axes from models.dev Moonshot catalog: reasoning toggle + effort `[low, high, max]`, no default; `_reconciliation_doc` records the source and reconciliation policy. **Corpus and tests** - 6 new normative-corpus vectors (canonical + one `goose-*` alias per new id); executable vector count updated to 113 in both Rust and TS gates. - Rust and TS label tests extended to cover all 3 new canonical ids and their aliases via the prefix stripper. - Sentinel variable fix in discovery-provider test to prevent false failure in Databricks dev environments where `BUZZ_AGENT_PROVIDER` is set. --------- 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> |
||
|
|
d274a6e949 |
fix(acp): guard against unrequested public relay skills (#6394)
Adds a shared base-prompt instruction that agents must not read or blindly follow public Buzz relay skills unless a human explicitly requests them. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
2a236e4137 |
refactor(prompt): simplify Buzz agent guidance (#6340)
## Why The Buzz agent prompt carries verbose agent-draft instructions that add prompt weight without improving benchmark correctness. Buzz-native channel trials also showed agents creating permanent channels while claiming they had applied a one-hour TTL. The static CLI inventory remains for efficient command discovery by smaller agents. ## What - Retain the static Buzz CLI command table and direct agents to use `--help` for full usage - Compact the owner-reviewed agent-draft guidance without changing its workflow - Clarify that `buzz channels create` is permanent unless `--ttl` is provided ## Benchmark Results - Terminal-Bench retained 22/22 correctness in the initial no-table candidate while reducing prompt size, input tokens, tool calls, and cost; total active time was effectively flat. - Across two Buzz-native runs, the non-channel cases tied at 26/30; the CLI clarification directly addresses the observed TTL omission. ## Update — 2026-08-20 - Restored the CLI command table following review feedback about multistep discovery with smaller agents. The restored-table candidate has not been re-benchmarked. Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
a9640c7cc4 |
Add Buzz-native collaboration benchmarks (#6264)
## Why Terminal-Bench can show task correctness while missing Buzz-specific collaboration failures, such as posting a final answer outside the user's thread. Add a small Buzz-native suite that scores relay behavior directly. ## What - Add Harbor tasks for direct thread replies, event-level callback mentions to a three-word user identity, targeted reads of explicitly named paths outside the workspace, and exact temporary-channel membership against a seeded directory of 50 users and 10 bots - Export sanitized post-agent relay evidence and extend solo runtime/provisioning for deterministic verification - Expose channel TTL in CLI search output and cover positive and negative transcript/state fixtures ## Risk Assessment Low to medium — this primarily adds benchmark and testbed machinery. The CLI change is an additive `ttl_seconds` field in channel-search JSON; relay behavior is unchanged. ## References - Motivated by the Buzz-specific behavior observed while benchmarking https://github.com/block/buzz/pull/6128 - The named-path regression case covers the issue being fixed by https://github.com/block/buzz/pull/6261 - Local live Sonnet runs: `reply-to-thread`, `user-mention`, and `create-channel-invite-users` each scored reward 1.0 with zero exceptions - The current branch prompt scored 3/5 on a concurrent `read-named-path-outside-workspace` batch: one explicit workspace-boundary refusal and one missing final response Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
934f3325c3 |
fix(buzz-acp): loosen workspace-scan guardrail to allow named paths (#6261)
## Problem Agents refused to read a specific, user-named path outside `.buzz` (e.g. `~/.claude/skills/context-health-check/SKILL.md`), and behaved inconsistently — refusing in one channel, listing the same dir in another. This is the issue Aaron and Wes hit: buzz://message?channel=e62570dd-33ad-42c5-b92b-75f2689f9694&id=401199157f8a9734109fdddfbbce4603a1ff465fae895042f29738ad52a5d15f Root cause: two prompt fragments told agents not to search `$HOME` "or other directories". The intent (added in #1194 by Will Pfleger) was narrow — stop unscoped `$HOME` sweeps that fire macOS TCC dialogs or make a model invent its own workspace dir. But "or other directories" reads as a blanket filesystem wall, so a cautious model generalized it to "any path outside `.buzz`" and refused a targeted, named read. ## Fix Prompt-only. Reframe both fragments per context-engineering guidance — state the reason and trust judgement, rather than banning a technique: **`crates/buzz-acp/src/pool.rs` — `workspace_section()`** > …This is where you already are, so start here rather than scanning `$HOME`. Any specific path the user names is fine to read. **`crates/buzz-acp/src/base_prompt.md` line 106** > These paths are relative to your working directory — start there for your own files rather than scanning `$HOME` or `/`. When the user names a specific path, read it. Preserves the original anchor intent (start at the grounded workspace; avoid unscoped `$HOME` sweeps) while explicitly permitting a named-path read — the exact case that was broken. ## Testing `cargo test -p buzz-acp` — 816 unit + 9 integration tests pass, 0 failures. Prompt-only change; the doc comment at `pool.rs:1327-1333` already states the narrow intent and is left as-is. Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz> |
||
|
|
7f69b13b45 |
fix(buzz-dev-mcp): expand leading ~ in read_file/str_replace paths (#6271)
## Problem `resolve_path` (crates/buzz-dev-mcp/src/paths.rs) only ever treats a path as absolute or joins it onto the workspace root. A user-named tilde path like `~/.claude/skills/context-health-check/SKILL.md` is not absolute, so it resolves to `<workdir>/~/.claude/...` (e.g. `/app/~/.claude/...`), which never exists. The `shell` tool expands `~` via bash, so the file tools diverged from it. Surfaced in the PR #6261 named-path benchmark: all five agents initially passed a literal `~` to `read_file`, which resolved incorrectly under `/app/~`. They recovered, but the ergonomics are wrong — agents already expect `~` to work; the resolver violated that expectation. ## Fix Expand a leading `~` (bare `~` or `~/...`) to the user home directory at the single chokepoint `resolve_path`, matching shell semantics. Home is read from `$HOME` (`%USERPROFILE%` on Windows). Both `read_file` and `str_replace` route through `resolve_path`, so both are fixed. - `~user` (another user home) is intentionally NOT handled — it needs a passwd lookup and is out of scope, consistent with the conservative posture for un-mappable MSYS forms already in this file. `~user...` falls through untouched and fails with the clear `path not accessible` error. - `expand_tilde` takes `home` as a parameter so it stays pure and unit-testable without mutating process environment; env lookup lives in a thin `home_dir()` helper. - Tool/param descriptions are intentionally left unchanged: `~` is normal shared path behavior, not a per-tool feature. Advertising it in each `resolve_path` caller's description would spend prompt tokens on an implementation detail and invite drift across callers. The tests on `resolve_path` document the behavior. ## Tests - `expand_tilde_forms`: pure coverage of non-tilde passthrough, `~user`/`~foo` passthrough, bare `~`, `~/rest`, and unset/empty home. - `resolve_path_expands_tilde_against_home`: end-to-end through the real `home_dir()` env read (creates a marker file under `$HOME`, skips cleanly if no home). - Full package suite green: `cargo test -p buzz-dev-mcp` — 98 passed, 0 failed. `cargo clippy -p buzz-dev-mcp --all-targets` clean. Closes #6270 --- ## Follow-up (review by @wpfleger): honor `$HOME` on Windows + translate MSYS home The first cut read `%USERPROFILE%` on Windows and `$HOME` elsewhere. That diverges from the shell tool, which is the whole point of this fix: - `HOME` is passed through to the MCP child on **every** platform (`buzz-agent` `PASSTHROUGH_ENV`, not cfg-gated) and inherited verbatim by the `shell` tool (`shell.rs` spawns bash with no `env_clear`). Bash expands `~` against `HOME`. So on Windows the file tools and shell tool disagreed whenever `HOME != USERPROFILE` — e.g. a git-bash `HOME=/c/Users/x`, or an `mcpServers[].env` override setting `HOME`. - Ordering also mattered: `~` expansion runs **after** `msys_to_windows` in `resolve_path`, so a spliced-in MSYS home (`/c/Users/x`) never got translated and `canonicalize` rejected it on Windows. **Change (`paths.rs` only):** - Prefer non-empty `$HOME` on every platform; fall back to `%USERPROFILE%` only on Windows when `HOME` is unset. - On Windows, run the chosen home through `msys_to_windows` so `HOME=/c/Users/x` → `C:\Users\x`. An MSYS home with no Windows equivalent (`/home/x`) is left untranslated and fails with the clear `path not accessible` error — the intended conservative outcome. - Selection factored into a pure `select_home(home, userprofile)` so it is unit-testable without mutating process env. **Tests:** `select_home_prefers_home` (HOME-first + empty/unset fallback, all platforms) and `select_home_translates_msys_home_and_prefers_it` (Windows: MSYS translation + divergent `HOME`/`USERPROFILE`, which also fixes the prior test's silent no-op when `HOME` is unset on CI). `cargo test -p buzz-dev-mcp` — 99 passed, 0 failed; clippy clean. Pre-push `rust-tests` + `desktop-tauri-checks` green. HEAD `29e0082ff`. --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz> |
||
|
|
196d62f97c |
fix(media): accept portrait video resolutions (#6058)
## Summary - apply the existing 2160×3840 video resolution envelope independent of orientation - accept portrait recordings whose short/long edges fit that envelope - retain rejection coverage for either edge exceeding its limit - make the relay error describe the orientation-independent limits ## Why The iOS simulator produces portrait H.264 recordings such as 1206×2622. The relay previously checked `width <= 3840 && height <= 2160`, so the equivalent portrait dimensions were rejected while landscape dimensions were accepted. ## Validation - `bin/just ci` - `cargo test -p buzz-media` (120 passed; one MinIO integration test ignored by its existing live-service guard) - `cargo clippy -p buzz-media --all-targets -- -D warnings` - exact blocked artifact `/Users/judeedwards/.buzz/.scratch/pr5874-mongo-evidence/native/head-20260816T050317Z/video-bitexact.mp4` validated through `validate_video_file`: 1206×2622 accepted Signed-off-by: Carl <5f365698229751c0461f57bb03a4e93134e6e936bd7039ebe7b737282a43c754@buzz.block.builderlab.xyz> Co-authored-by: Carl <5f365698229751c0461f57bb03a4e93134e6e936bd7039ebe7b737282a43c754@buzz.block.builderlab.xyz> |
||
|
|
08eb46ef3c |
Revert "fix(acp): gate relay-signed workflow messages on their attributed author" (#6311)
Reverts block/buzz#6129 |
||
|
|
121e4b3ce7 |
fix(desktop): exclude archived agents from nest, order regeneration (#5905)
The managed "Active Agents" table in `~/.buzz/AGENTS.md` was rendered from every managed-agent record with no filtering, so archived duplicate instances kept appearing under the active relay's header. This scopes the roster to identity-active agents and makes regeneration safe under concurrency. ## Roster filter: identity-archive only `render_dynamic_section` now drops only records whose pubkey is present in the relay's `kind:13535` archive snapshot. Local records can't tell they're archived — they all carry `is_active: true` (that flag is a *definition*-archive, not an identity-archive), so archive truth lives only relay-side. The read **fails open**: an unreachable relay yields an empty set and hides no one. There is deliberately **no** relay-scope filter. `relay_url` is a legacy creation-era field that `effective_agent_relay_url()` ignores — every agent is eligible on every community, and snapshot-imported records store `relay_url: ""` by design. Filtering on it would hide valid, runnable agents after a workspace switch or import. Foreign-relay relic records leave the table via record deletion, not code. ## Regeneration on archive / unarchive `archive_identity` and `unarchive_identity` submitted the relay event and returned without refreshing `AGENTS.md`, unlike the ~20 other mutation sites that call `try_regenerate_nest`. A just-archived agent therefore lingered on the roster until an unrelated edit or the next launch. Both commands now trigger a regeneration after a successful submit. Regeneration is bound through a `NestRegenTrigger` trait rather than constructing the `try_regenerate_nest` callback at the Tauri-command delegation site. The command cores take `regen: &impl NestRegenTrigger` and own the `|| regen.trigger()` binding; the thin wrappers only pass `&app` (whose `impl` calls `try_regenerate_nest`). This puts the regen wiring inside the unit-tested core — a `CountingRegen` double proves each core fires exactly one regeneration — instead of an untestable seam where a wrapper could silently lose the refresh while the suite stayed green. The regen races the relay's `kind:13535` snapshot update, so it's best-effort and fail-open — a stale render self-heals on the next cycle. ## Ordered regeneration `try_regenerate_nest` previously spawned unconstrained tasks that each snapshotted state, awaited two relay requests, then wrote — so a slow pre-edit generation could overwrite a newer one. Boot made this deterministic: the boot regen races the `apply_workspace` regen, and the fallback-relay render could finish last. `NestRegenGate` fixes this with a single `highest_requested` watermark. A monotonic generation is claimed *synchronously* at request time (encoding call order) and advances the watermark under one lock; the spawned task carries its generation and, at commit, reads the watermark under that same lock — the compare-and-write is atomic with no await held across it. A generation whose number is below the current watermark drops its result instead of rolling the file back. Gating on highest-*requested* rather than highest-*written* is the load-bearing choice: if a newer generation is requested but then fails its relay reads, an older in-flight generation must **not** publish its now-obsolete roster. Behavior delta: once a newer regeneration has been requested, no older generation will ever write; if that newer generation fails, the file is left as-is and self-heals on the next trigger rather than regressing to a stale snapshot. This is an ordered, latest-request-wins gate — not a work coalescer: superseded generations still perform their relay reads and drop the result at commit time. The gate's commit lock is acquired with a poison-to-`io::Error` mapping rather than `expect()`, so a poisoned lock degrades to the same warn-and-continue path as any other commit failure instead of panicking the desktop process (a best-effort housekeeping write must never take down the app). ## One relay target per regeneration A regeneration read the workspace relay override three times — the NIP-11 signer in `fetch_relay_self`, the snapshot query in `query_relay`, and the rendered footer — so a workspace switch mid-flight could pair one relay's advertised signer with another relay's snapshot, fail open, and render archived agents as active. `capture_relay_target` now resolves the effective relay (WebSocket + HTTP API base) once, before any network work, and `fetch_archived_pubkeys_at` threads that single target through both the NIP-11 fetch and the `/query`; the footer renders the same target. Signer, snapshot, and footer always belong to one relay. ## Monotonic archival snapshot publishing `publish_nipia_archival_list` stamped its `kind:13535` snapshot with a whole-second `created_at`. A rapid archive→unarchive within the same second produced two events whose NIP-16 replaceable-event tie-break (higher event id wins) could strand the older, stale archive state as canonical. Publishing now uses a bounded retry that re-reads current archive state and rebuilds the snapshot each attempt, so the published list reflects the latest intended state rather than a fixed same-second race loser. After the attempt budget (8) is exhausted the publisher `bail!`s; the sole caller treats that as a `warn!` side effect and continues, matching the surrounding best-effort submit path. ## Test-file split The renderer, `upsert_managed_section`, and the regeneration-gate tests moved to `nest/render_tests.rs` so each test file stays under the repository's 1000-line ratchet. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
50a71137e6 |
feat(managed-agents): close five Claude Code agent-config gaps (#4557)
Closes five Claude Code agent-config gaps in Buzz Desktop, split into three commits that share the spawn-time and live-switch surfaces. ## Config isolation, model authority, and Auto mode (#2692, #2884, #3493) - **`CLAUDE_CONFIG_DIR` isolation (#3493).** `config_bridge` resolves both `settings.json` and `.claude.json` panel paths from the agent's effective env (`resolve_effective_agent_env` — baked floor → definition → global → persona → record), so the panel reads the same directory the agent runs against. `mcp_config_file_path_for_runtime` honors a custom dir; empty/blank is treated as unset, matching Claude's `CLAUDE_CONFIG_DIR || homedir()` semantics. `AgentConfigPanel` shows a Keychain caveat when a custom dir is active (a custom dir maps to a fresh Keychain namespace unless `CLAUDE_SECURESTORAGE_CONFIG_DIR` is also set). - **Single startup model authority (#2692).** `ANTHROPIC_MODEL` is the sole startup model authority for Claude. Local spawns write `ANTHROPIC_MODEL` and strip `BUZZ_ACP_MODEL` so the harness never sees two authorities; remote deploys send `ANTHROPIC_MODEL` in `policy_env` instead of `BUZZ_ACP_MODEL`. Non-Claude runtimes are unchanged. - **`PermissionMode::Auto` (#2884).** Wire string `"auto"`, model-gated, degrades to the agent default when the active model doesn't advertise it. ## Thinking effort end-to-end for local Claude agents Effort flows from the running session's discovered `thought_level` config option through the config surface to a local-only write control and a read-only two-facts display. - **Reader.** Discovers the `thought_level` config option from the session cache (never hardcoded) and populates `effort_config_id` / `effort_options` on `RuntimeConfigSurface`. The canonical effort tier orders record env > `record.effort_level` (`BuzzExplicit`) > ACP > persona > global > definition > file, so the panel shows the effort the next spawn will launch with while `resolve_with_override` surfaces the running ACP value as the struck-through override — neither masks the other silently. - **Write control.** `persist_agent_effort_level` is a direct-write Tauri setter (writes `record.effort_level` + `updated_at`, `save_managed_agents`) that rejects non-local backends — remote effort is set at deploy time via `policy_env`. `EffortPickerField` mounts in `AgentInstanceEditDialog` beside the Model block, gated on a local backend **AND** a discovered `effortConfigId`. It persists directly and invalidates the config surface, mirroring the `setManagedAgentAutoRestart` standalone-setter precedent, so the frozen `UpdateManagedAgentInput` shape stays frozen. - **Display.** The read-only configured-vs-current two-facts display is the `thinkingEffort` normalized field in `AgentConfigPanel`, fed by the reader's canonical tier ordering. - `buzz-acp` applies the startup effort env at session start. ## Distinguish a rejected model switch from silent success A live model switch collapsed adapter rejection into success: `apply_model_switch` returned `Ok` on both an accepted switch and an application-level refusal, so the caller cached pre-switch capabilities as if they described the target model and Desktop reported the pick as landed. - `ModelSwitchOutcome::{Applied(Value),Rejected}`. Transport-class errors still propagate as `Err` (respawn the poisoned stdio); an application-level refusal is now `Rejected`. - The caller drives everything off `post_switch_snapshot`: `Applied` refreshes `model_capabilities` from the target model's echoed `configOptions` (or drops to `None` when none are echoed, so a pre-switch snapshot is never mistaken for the target's); `Rejected` preserves pre-switch caps and emits a `failure` `control_result`. Effort resolution and the session-config capture read the post-switch snapshot so they converge on the model the session actually runs; `modelOverridden` is gated on `switch_succeeded`. - `liveSwitchOutcome.ts` gains a distinct `"failed"` outcome for the adapter `failure` frame and treats the busy-path `"sent"` ack as provisional — it never counts toward success. Success is confirmed only by a real positive terminal frame (the busy-path deferred apply emits a correlated `switched` `control_result` when it lands), and the fallback timeout resolves an honest `"pending"` (accepted, apply deferred), never a false `"ok"`. `ModelPicker` surfaces a distinct toast per outcome — failed, unsupported, and pending. ## Scope explicitly excluded Per-agent config dir provisioning, `CLAUDE_SECURESTORAGE_CONFIG_DIR` sentinel injection, `settings.json` projection, protected-key stripping, MCP inheritance, spawn serialization, and the `last_spawn_warnings` surface are absent from this diff. Silent-fallback machinery for non-Claude runtimes (#2265/#4004) is a tracked follow-up. ## Sanctioned follow-ups - **Live mid-conversation effort switching.** Effort is spawn-scoped only in this PR: the worker reads `BUZZ_ACP_EFFORT_LEVEL` once and applies it at session creation. The live effort-switch machinery (mid-conversation effort RPC + ack frame) was deliberately removed and is archived on `archive/claude-config-gaps-live-effort` for a future plan-gated revival. - **Idle-path late model-switch rejection is unobservable.** An idle switch acks `switched` immediately after catalog validation, but the real `set_config_option` runs at the next session creation — potentially much later — so a rejection there is not surfaced back to the picker (holding a subscription that long is not sensible). Pre-existing, catalog-gated behavior; a durable fix is a tracked follow-up. Closes #2692, #2884, #3493 --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
d2cfd377e2 |
fix(prompt): simplify pickup follow-through (#6186)
## Why The base prompt prescribed a todo lifecycle for every task needing follow-up tools, which added control calls even for work completed in one turn. This keeps the important behavioral contract—continue after publishing pickup—without prescribing the mechanism. ## What - Replace the 40-word todo lifecycle with a concise pickup follow-through rule - Preserve the requirement to publish the outcome or blocker before stopping ## Risk Assessment Low to medium — this changes managed-agent instructions, not runtime code. The terminal benchmark covers single-turn task completion but does not cover restart, compaction, or long-lived multi-turn recovery. ## References - Builds on the prompt simplification in https://github.com/block/buzz/pull/6161 - Benchmark setup: GPT 5.6 Terra at high effort; the same 11-task Terminal-Bench 2.1 slate; four concurrent trials; 4 CPU and 8 GiB per trial; 3× timeout | Prompt | Pass | Active-h | Median active | Tool calls | |---|---:|---:|---:|---:| | PR 6161 baseline | 22/22 | 0.3438 | 0.91 min | 216 | | Benchmarked 14-word rule | 22/22 | 0.2926 | 0.75 min | 198 | The benchmarked rule used 14.9% less active time, 16.9% lower median active time, and 8.3% fewer tool calls. Across the screen and confirmation runs it passed 33/33 trials with every completion report present; results are directional because model and tool behavior is stochastic. --- **Update Aug 18, 10:47 EDT:** Expanded the completion outcomes following review feedback. - The follow-through rule now covers a verified result, blocker, or key decision or information that needs to be surfaced. - The benchmark was not rerun; the table reflects the prior 14-word formulation. This is a completion-taxonomy clarification, not a return to a prescribed todo mechanism. Generated with Codex Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
5694e78def |
fix(mcp): scope todo usage (#6216)
## Why The `todo` tool description does not say when the tool is unnecessary, so agents use it for single-turn bookkeeping. Scoping it to cross-turn persistence reduces avoidable control calls while preserving the checklist for compaction and genuinely multi-turn work. ## What - Scope `todo` to work that must continue across turns or survive context compaction - Tell agents not to use it for work they can finish in the current turn - Preserve read/replace semantics and the `_Stop` hook behavior ## Risk Assessment Low to medium — this changes agent tool-selection guidance, not the tool name, schema, or implementation. The benchmark covers single-turn completion but not restart, compaction, or long-lived multi-turn recovery. ## References - Companion base-prompt change: https://github.com/block/buzz/pull/6186 - Combined benchmark (PR 6186 prompt plus this description): 22/22 pass; active time 0.3438h → 0.2680h (-22.0%); tool calls 216 → 173 (-19.9%); todo calls 45 → 0 - Against PR 6186's prompt-only condition: active time 0.2926h → 0.2680h (-8.4%); tool calls 198 → 173 (-12.6%) - Results are directional because model and tool behavior is stochastic. Generated with Codex Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
f7a01bda7b |
fix(workflows): preserve multi-channel listing semantics (#6009)
**Category:** fix **User Impact:** Workflow listings reliably include every accessible channel, including for users with more than 128 memberships and when connected to older relays. **Problem:** Multi-value `#h` filters could lose live delivery, apply channel scoping after SQL limits, mishandle partial authorization or revocation, and permit unbounded membership work. Desktop also submitted every channel in one request, exceeding the relay's new 128-value safety bound. **Solution:** Preserve NIP-01 OR semantics across relay query, count, and live-subscription paths while enforcing authorization and bounded explicit-channel work before database or Redis operations. Desktop keeps the older-relay-compatible one-channel-per-filter shape, sends filters in bounded batches, combines responses, and deduplicates signed events by event ID. <details> <summary>File changes</summary> **crates/buzz-db/src/event.rs** Distinguishes authorization channel scopes from explicit `#h` scopes in list and count SQL so requested channels are applied before limits without implicitly including global rows. **crates/buzz-relay/src/handlers/req.rs** Shares explicit-channel scope extraction and limits, preserves valid OR siblings when malformed branches cannot match, repairs request-local membership misses, and registers authorized live subscriptions per channel. **crates/buzz-relay/src/handlers/count.rs** Applies the same bounded explicit-channel authorization to COUNT and preserves channel scope when a multi-channel request narrows to one authorized channel. **crates/buzz-relay/src/api/bridge.rs** Brings HTTP query and count behavior in line with WebSocket semantics before SQL execution and rejects over-limit explicit-channel requests before membership I/O. **crates/buzz-relay/src/subscription.rs** Indexes multi-channel subscriptions by every authorized channel and shrinks, rather than destroys, their scope when one channel is revoked. **crates/buzz-relay/src/handlers/side_effects.rs** Releases only revoked channel topics and sends terminal closure only when no authorized channel remains. **crates/buzz-test-client/tests/e2e_relay.rs** Adds ignored relay integration coverage for multi-channel delivery and valid historical/live behavior with malformed or empty OR siblings. **desktop/src-tauri/src/commands/workflows.rs** Builds one single-channel filter per membership, submits at most 128 per relay request, combines batches, and deduplicates by immutable signed event ID. **desktop/src-tauri/src/commands/workflows_tests.rs** Covers filter compatibility, malformed input, 129-channel batching, and cross-batch event-ID deduplication. </details> ## Reproduction steps 1. Join multiple channels containing workflows, open **Workflows**, and confirm workflows from every accessible channel appear. 2. Repeat with more than 128 memberships and confirm the listing remains complete rather than failing the relay request. 3. Send a multi-value `#h` query/count and confirm only requested authorized channels affect SQL limits and counts. 4. Subscribe to channels A and B, revoke A, and confirm B continues delivering live events. 5. Subscribe with a valid channel branch plus a malformed or empty `#h` sibling and confirm valid history, EOSE, and post-EOSE live delivery still occur. ## Validation At pushed head `c419a923f05e483ab26c006a0b3a80cfb3c73844`: - Relay request tests: 53 passed. - Desktop full Rust unit suite: 2,468 passed, 17 ignored. - Relay E2E target compiled with `--no-run`. - Strict relay clippy passed. - Desktop Tauri clippy/check passed. - Pre-push Rust tests and Desktop Tauri checks passed. - Rust formatting and `git diff --check` passed. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> |
||
|
|
f64899e5d1 |
Remove Startup Recovery section in base prompt (#6161)
## Why Managed channel sessions already receive authoritative per-turn context. The old startup recovery checklist told every new session to scan the global feed ## What - Remove `Startup Recovery` with concise channel and heartbeat turn contracts. ## Risk Assessment Low. This changes prompt guidance and its test only; routing and runtime behavior are unchanged. Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
a282e0643f |
fix(cli): keep project replacement timestamps at or after wall clock (#5666)
Fixes #5665. `next_timestamp` in `crates/buzz-cli/src/commands/projects.rs` computed a replacement's `created_at` as `head.created_at + 1`. The relay's ingest path rejects events more than ±900s from server time (`MAX_TIMESTAMP_DRIFT_SECS` in `crates/buzz-relay/src/handlers/ingest.rs`), so: - `projects update` on any project whose head is older than 15 minutes fails with `relay error 400: invalid: event timestamp too far from server time` (live repro in #5665); - inside the window, replacements are recorded at `head+1` — seconds-to-minutes in the past — so a concurrent wall-clock writer silently wins LWW and audit timestamps misstate when the write happened. ## Change `next_timestamp` now returns `max(now, head.created_at + 1)`: strictly after the observed head (preserving the dominate-the-head guarantee for skewed/future heads), never behind the wall clock. This mirrors the relay's own replacement-authoring pattern (`now.max(head+1)` in `side_effects.rs`). ## Testing - `cargo test -p buzz-cli --lib` — 344 passed; adds `next_timestamp_uses_wall_clock_when_head_is_stale`, and the existing far-future-head test still holds (`head+1` wins when head > now) - `cargo clippy -p buzz-cli --all-targets` / `cargo fmt --check` — clean - Live before/after on a self-hosted relay: vanilla CLI fails on a 2h-aged head; with this change the same update is accepted and the head lands at wall clock. Same failure family as #2876 (`repos protect` vs the drift window) — that path is not touched here. --------- Signed-off-by: Ika Minami <ika@infiniteidol.com> Signed-off-by: Ravneet Arora <rarora@squareup.com> Co-authored-by: Ika Minami <ika@infiniteidol.com> Co-authored-by: Ravneet Arora <rarora@squareup.com> |
||
|
|
54f11219ef |
fix(acp): gate relay-signed workflow messages on their attributed author (#6129)
## Problem Scheduled workflow `send_message` actions fire and land in the channel with correct `p` tags for the mentioned agents — but the agents never wake. The wake-up is silently dropped. **Root cause:** workflow messages are signed by the **relay keypair** (`workflow_sink.rs` signs with `state.relay_keypair`), so `event.pubkey` is the relay's pubkey, not the workflow owner. In `buzz-acp`, the inbound author gate (`author_allowed`) runs **before** the `p`-tag mention check. Under the default `respond_to = owner-only`, the relay pubkey is neither the owner nor a sibling, so every workflow wake-up dies at the gate with a debug-level `"inbound author gate — dropping event"`. The relay-side comment even says the mention `p` tags exist *"so mentioned agents are woken (wake is p-tag gated)"* — but wake is also author-gated, and that path was missed. ## Fix Gate relay-signed workflow messages on their **attributed author** — the pubkey that created the workflow — instead of the relay pubkey: - **Relay:** `workflow_sink.rs` now emits an explicit `buzz:workflow-owner` tag carrying `workflow.owner_pubkey` (the workflow creator, which the executor already passes as `author_pubkey` and whose channel access the relay verifies before emitting). Ownership is never inferred from `p`-tag order; mention `p` tags play no role in attribution. - **Harness:** at startup, `buzz-acp` fetches the relay's NIP-11 `self` pubkey (new `RestClient::fetch_relay_self`, public `/info` endpoint). Best-effort: fetch failure just logs a warning and preserves pre-fix behavior. - **Gate:** an event that is (a) authored by the relay `self` key, (b) tagged `buzz:workflow`, and (c) carries a well-formed `buzz:workflow-owner` pubkey is gated on that owner, through the exact same owner/sibling/allowlist policy as a direct author. ## Security notes (all fail closed) - No NIP-11 `self` pubkey → no exemption. - `buzz:workflow` / `buzz:workflow-owner` tags on a non-relay-signed event → ignored (a member cannot forge the exemption; the relay verifies signatures on submission and only the relay holds its key). - Relay-signed event without the tags, or with a malformed owner value (not 64-hex) → plain author gate. - Who is @mentioned in the message has no bearing on whose authority is evaluated. - A workflow owned by a random channel member still cannot wake an owner-only agent — the owner's pubkey must pass the same policy. ## Testing - 7 unit tests (`workflow_attributed_author_tests`) covering attribution, fail-closed paths, p-tag independence, malformed owner values, and the forgery case. - Extended the PG-gated `workflow_send_message_p_tags_mentioned_member` integration test to assert the `buzz:workflow-owner` tag. - `cargo test -p buzz-acp`: 785 passed, 0 failed. `cargo test -p buzz-relay --lib workflow_sink`: 17 passed. Clippy + fmt clean. (9 pre-existing `buzz-relay` failures in unrelated `api::media`/`api::admin` tests fail identically on the base commit without this change.) Found while debugging scheduled automations in a Buzz review-pipeline channel: two cron workflows fired daily @mentions at agents that never responded, while direct human @mentions woke them instantly. --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> Co-authored-by: Fizz <3a9f8a30fbb462abec1e2977b2280a7ae50c7ff794433790be15bd48bfd52d0b@buzz.block.builderlab.xyz> |
||
|
|
5b3f0375a2 |
fix(acp): replace Goose native system prompt (#5964)
## Why Buzz currently appends its managed prompt to Goose's native prompt, so managed agents receive both instruction sets instead of the intended Buzz-only system prompt. ## What - Send Goose's custom session system-prompt request with `mode: "set"` - Lock the replacement contract in the ACP request test ## Risk Assessment Low — the change is limited to Goose session setup; adapters that do not implement Goose's custom method keep the existing method-not-found fallback behavior. ## References Goose v1.46.0 routes `set` to `override_system_prompt`, and its prompt builder selects that override instead of rendering the native `system.md`: [ACP handler]( |
||
|
|
edc4a09aaa |
feat(workflows): add responsive library card actions (#6008)
**Category:** improvement **User Impact:** Users can scan what each workflow does and trigger, edit, duplicate, enable, disable, or delete it directly from the library. **Problem:** The workflow list buried common actions and did not expose each automation's trigger-to-action shape at a glance. **Solution:** Add a responsive workflow library with a persistent create tile, compact trigger/action diagrams, prominent workflow titles with supporting descriptions, and shared card actions while preserving existing detail, editor, and run-history entry points. Card toggles refresh both list and open-detail caches so status and definition stay consistent. <details> <summary>File changes</summary> **desktop/src/features/workflows/ui/WorkflowActionsMenu.tsx** Adds a shared card menu for trigger, edit, duplicate, enable/disable, and delete actions. **desktop/src/features/workflows/ui/WorkflowCard.tsx** Reworks cards around the prototype's visual hierarchy: color-coded trigger, action flow, sentence-case eyebrow, prominent title, supporting description, status, channel, and update date without a footer clock icon. **desktop/src/features/workflows/ui/WorkflowsView.tsx** Adds the responsive grid, create tile, mutation wiring, and list/detail cache invalidation. Container breakpoints keep cards two-across at medium widths and three-across in the 1280px desktop layout. **desktop/src/features/workflows/ui/workflowDefinition.ts** Adds immutable enabled-state updates plus narrow trigger and first-action readers used only to select card icons. **desktop/src/features/workflows/ui/workflowDefinition.test.mjs** Covers neutral icon selection, enabled-state immutability, and status presentation. **desktop/tests/e2e/workflows.spec.ts** Covers the create tile, title/description hierarchy, selected-card enable/disable consistency, and deterministic narrow/medium/wide captures while retaining existing action coverage. </details> ## Reproduction steps 1. Open **Workflows** and confirm the create tile stays first as cards flow from one to three columns with available width. 2. Confirm each card shows a sentence-case trigger eyebrow, prominent workflow title, supporting description when present, status, channel, and update date without a clock icon. 3. Open a card's overflow menu and trigger, edit, duplicate, enable/disable, or delete the workflow. 4. Leave the detail panel open while toggling and confirm its badge and JSON definition update with the card. ## Screenshots Real built E2E UI with representative workflow data at three viewport sizes. ### Narrow — 800 × 720  ### Medium — 1024 × 720  ### Wide — 1280 × 720  ### Card actions  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> |
||
|
|
1b7e5ac1be |
feat(model-capabilities): drive model capabilities and labels from one manifest (#5597)
## Summary Centralizes model capability knowledge — thinking mode, supported effort levels, wire routes, and human-readable labels — into a single manifest, `scripts/model-capabilities.json`. Rust and TypeScript each get a small interpreter that reads the same manifest, replacing hand-maintained tables scattered across both languages that had already drifted apart. A capability change is now a data edit, not parallel edits to two code paths. Supersedes the codegen approach explored in #3603. A cross-language contract keeps the two interpreters honest: `scripts/normative-corpus.json` is a golden snapshot generated from the Rust resolver (103 vectors covering all six capability axes) and replayed natively in TS. CI fails if either language disagrees with the corpus or the corpus drifts from the resolver. Regenerate with `just regen-model-corpus`. ## Behavior changes - **Effort dropdown for `openai-compat` providers** no longer offers `max`. The request path always clamped `max` to `xhigh` on the wire, so the UI stops offering a value that was silently rewritten. UI-only, wire-identical. - **Databricks v2 routing (wire-visible):** uncurated endpoint names carrying a bare Claude code-name segment (e.g. `goose-opus-5`) now route to the MLflow chat wire instead of Anthropic Messages — they lose Anthropic prompt caching but still succeed on a valid OpenAI-compatible wire. Curated `databricks-claude-*` records and any name starting with `claude` are unchanged. A handful of other uncurated/adversarial name shapes similarly fall back to MLflow chat instead of pattern-matched routes; every curated model resolves identically to before, all axes. - **Curated model labels on the real discovery path.** The Databricks API returns no display name, so discovery emits the raw endpoint id as the model `name` (`{id, name: id}`) on every path. `ModelEntry.name` is now curated at all four construction seams in `buzz-agent` — v2 discovery, v1 parse, the auth-empty default catalog, and the configured-model fallback — via a read-only `databricks_registry_label` lookup over the manifest's `databricks_v2` exact records; `id` stays the raw wire/config value. A known id renders its curated label (`databricks-gpt-5-5` → `GPT-5.5`), an unknown id passes through unchanged, and the default-catalog row reads `GPT-5.5 (default catalog)`. As a defense against older `buzz-agent` binaries and any harness that echoes ids, `resolveModelLabel` treats a discovered name equal to the trimmed id as absent and falls through to the registry tier; a genuinely distinct name (including the suffixed default-catalog label) still wins. ## Cleanup Deletes the duplicated capability tables and their tests: the `config.rs` gpt5 matchers, effort tables, and clamp logic; the legacy segment-based Databricks v2 route classifier in `llm.rs`; and the TS hand tables plus `effortTable.fixture.json`. All are replaced by manifest lookups through the shared resolver — no line of capability data exists in two places. --------- 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> |
||
|
|
122a8b8988 |
Projects v3: unify sharing, discussions, and issue ownership (#5792)
## Summary Projects v3 makes repository work shareable, discussion-aware, and easier to scan in one coherent workspace. People can copy canonical links, reopen the exact workspace tab, understand issue and pull-request context at a glance, find related channel conversations, and assign or unassign issues across Desktop and CLI. - **Unified workspace** — top-level sections sit above repository controls in one rounded workspace, with navigation positioned close to the page heading. README and Files retain branch selection; every section has a labeled icon header, and Issues and Pull Requests expose creation from a consistent right-aligned action. - **Repository management** — the repository selector is always available, including single-repository projects. Its integrated add flow lets project owners create a repository manually or select an existing repository without a separate toolbar button. - **Readable work-item lists** — issue and pull-request rows use plain-language context instead of opaque metadata. Files, commits, issues, pull requests, channels, and contributors share consistent row density and right-aligned timestamps, while deterministic fallback-avatar colors keep participants distinct on light backgrounds. Inbox pull-request metadata wraps between complete phrases and truncates long channel names instead of compressing copy into narrow columns. - **Reliable entity links** — projects, repositories, issues, pull requests, and commits have canonical `buzz://` links, preview cards, OS deep-link routing, and tab-aware navigation. Reopening the same link re-applies its destination instead of leaving the user on a locally selected tab. - **Related conversations** — repository and work-item views surface channels discussing the current entity, including participants, channel navigation, message context, and an explicit notice when discovery reaches its 500-result cap. - **Reversible issue ownership** — trusted assignment and unassignment events work across Desktop, Tauri, `buzz-sdk`, and `buzz issues`. Assignees appear in project views and the assigned inbox, while authorized users can remove assignments directly from the assignee row. Assignment state is derived chronologically from labeled Nostr notes. Issue authors and repository owners may change any assignee; other users may only assign or unassign themselves. Shared golden fixtures keep entity-link grammar and validation aligned across TypeScript and Rust. The branch also updates `webbrowser` to the patched release for RUSTSEC-2026-0257. ### Related issue N/A. ### Testing - [x] `just ci` — formatting, lint, typechecking, unit tests, and builds passed - [x] Full pre-push suite — organization, branch-skew, Desktop checks, typechecking, and tests passed on the latest push - [x] `cargo test -p buzz-cli` and focused `buzz-sdk` assignment tests passed - [x] Focused Tauri recipient-note and 500-result search-limit tests passed - [x] Desktop entity-link and issue-assignment unit tests passed - [x] Playwright smoke coverage passed for assignment, repeated entity-link navigation, repository create/select flows, section headers and actions, timestamp alignment, timeline icons, sentence-style issue/PR metadata, header spacing, avatar contrast, and Inbox metadata at stacked and side-rail breakpoints - [ ] Manual staging pass: link round-trips, Channels tab, assignment flows, and inbox routing ### Screenshots Pull requests explain who opened the request, where it lives, and which branch it comes from; fallback avatars remain visually distinct.  Issues use the same sentence-style hierarchy while keeping status and recency easy to scan.  The wide Inbox detail keeps author, timestamp, and origin context readable beside its metadata rail.  [View the complete six-state Projects v3 screenshot set](https://github.com/block/buzz/pull/5624#issuecomment-5268039672) and [the compact/wide Inbox comparison](https://github.com/block/buzz/pull/5624#issuecomment-5268614585). --- > Supersedes #5624, whose head commit accumulated permanently-queued required check suites (block-dco-check et al.) that GitHub never dispatched. History flattened into a single signed-off commit on latest main; tree verified byte-identical (`git merge-tree`) to merging the original branch into main. --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Co-authored-by: Wintermute <3f1797424fd9ad6653a83665c660517777cd7f8c228c0d5907f49e01537f3ca5@buzz.block.builderlab.xyz> |
||
|
|
1d51081b8a |
Teach agents to inherit Buzz product intent (#5875)
## Summary - make `VISION.md`, relevant `VISION_*.md`, and applicable testing guides explicit planning and review inputs for non-trivial Buzz changes - teach managed agents to load repository-root and path-local `AGENTS.md` files after selecting a checkout - distinguish CI evidence from exercising the live workflow for user-visible and integration behavior - turn repeatable mistakes into same-session durable lessons, keeping only load-bearing rules in core memory and promoting shared lessons to team guidance - pin the new managed-agent prompt invariants in tests - preserve the exact display name shown in Buzz when mentioning or addressing someone; never infer or look up a surname merely to sound more complete ### Related issue None found after searching `block/buzz` issues and PRs for agent instruction, vision, and product-intent routing. ### Testing At commit `07ef705b42f58d3be6981165c6959d541ada0ba7`: - `cargo fmt --all -- --check` - `cargo test -p buzz-acp agent_draft_prompt_tests` (4 passed) - mandatory pre-push hooks passed on the exact pushed head: `branch-skew`, `desktop-check`, `desktop-typecheck`, `mobile-test`, `desktop-test`, `rust-tests`, and `desktop-tauri-checks` - `git diff --check origin/main...HEAD` --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
068a83b097 |
feat(huddle): cut voice-turn time-to-first-audio from ~1.0 s to ~0.35 s (env-gated latency levers) (#5671)
## Overview
**Category:** feat (env-gated experiment + one exact always-on
optimization)
**Problem:** Speech-end -> first TTS audio through the desktop huddle
pipeline measures **924–1087 ms** on an M4 Max with a 0 ms LLM leg.
Voice turns feel sluggish no matter how fast the agent replies. Baseline
breakdown: ~300 ms hardcoded VAD silence flush + ~150–250 ms Parakeet
decode + ~380–550 ms TTS synthesis before the first player append.
**Outcome:** With all levers enabled, e2e time-to-first-audio measures
**347–384 ms** (307–357 ms on a longer utterance) on the same hardware,
harness, and production pipelines. Defaults preserve production behavior
everywhere except one deterministic, bit-exact cache win.
## What's in here
### Levers (all default-off, env-gated)
| Lever | Env | Effect (measured medians, short utterance) |
|---|---|---|
| Speculative Parakeet decode | `BUZZ_STT_SPECULATIVE=1` | STT leg ->
~max(flush, decode) |
| Streaming TTS synthesis | `BUZZ_TTS_STREAMING=1`,
`BUZZ_TTS_EMIT_FRAMES` | first audio 380–550 -> 211–320 ms (emit=12,
bit-exact) |
| ONNX intra-op threads | `BUZZ_STT_THREADS`, `BUZZ_TTS_THREADS` | TTS
first audio 211–320 -> 129–180 ms (4 threads) |
- **Speculative decode** starts the Parakeet decode at the *first*
silent VAD frame, overlapping it with the flush window. Resumed speech
invalidates the result (voiced-frame-count check); held silence emits it
instantly at the flush boundary.
- **Streaming TTS**: new `synth_chunk_streaming` (buzz-voice)
interleaves the Flow LM frame loop with incremental *stateful* Mimi
decoding, emitting PCM deltas to the player via the existing
`PlaybackChunkAudio` decoration. At `emit_frames=12` (the decoder's
native chunk) streamed audio is **bit-identical** to the batch path —
verified by the ignored test
`incremental_stateful_decode_matches_batch_decode` (max|diff|=0).
Smaller deltas are faster but diverge (~23 dB SNR; decoder intra-chunk
lookahead), hence the default of 12.
> **Removed after live testing:** the `BUZZ_STT_FLUSH_MS` flush-window
override. Lowering the silence window below natural mid-sentence pauses
(the fast-path recipe said 150 ms) split single spoken sentences into
multiple messages and confused the listening agents. The window is a
turn-taking quality knob, not a latency lever — it is now fixed at the
production 300 ms value.
### Push-to-talk grouping fix (always-on)
A held push-to-talk shortcut is an explicit "I am not done talking"
signal, so silence never ends the utterance while it is held — even when
the microphone is also manually open. The utterance flushes on shortcut
release (existing transmit-edge flush); a manually open mic with the
shortcut up keeps normal VAD pause flushing. Gate is the pure
`vad_flush_allowed` function with a unit-test truth table.
### Always-on (exact): voice-conditioning cache
Phase profiling (`BUZZ_TTS_PHASE_LOG=1`) showed a fixed ~160 ms
`condition_voice` Flow-LM pass on *every* chunk, re-deriving the same
post-conditioning state for the same reference voice. The state is now
snapshotted after first computation and restored per chunk (dtype-tagged
tensor copies, keyed identically to the existing `cached_voice`).
Deterministic — same tensors in, same tensors out. The default path's
TTS leg drops from 380–550 ms to 225–355 ms with no configuration.
### Bench harness
`huddle::latency_bench` (`#[cfg(test)]` + `#[ignore]`) drives the real
`SttPipeline` and `TtsPipeline`, feeding a 48 kHz WAV in real-time 100
ms batches (AudioWorklet cadence) with a configurable fake LLM in place
of the relay leg, timing speech-end -> transcript -> speak() -> first
accepted player append.
```
BUZZ_STT_SPECULATIVE=1 BUZZ_TTS_STREAMING=1 \
BUZZ_TTS_THREADS=4 BUZZ_STT_THREADS=2 \
BUZZ_BENCH_WAV=<48k f32 mono wav> \
cargo test --release -p buzz-desktop --lib huddle::latency_bench -- --ignored --nocapture
```
## Tradeoffs to weigh before promoting any lever to a default
- **Speculative decode**: the speculative buffer has ~1 silent tail
frame vs ~19; observed one CTC wobble ("fail" vs "failed") in 24 turns.
Mitigation if productionized: zero-pad the speculative buffer to match
the flush-path shape.
- **Threads**: defaults stay 1 pending the min-spec (4-core Intel) A/B
flagged in the existing `STT_NUM_THREADS` comment.
- **Streaming at emit<12** is NOT the same waveform — don't ship below
12 without an ear pass.
## Validation
- Full desktop lib suite: **2408 passed / 0 failed** at this head
(`18fab2e1c`).
- buzz-voice suite green; bit-exactness test passes against the
production batch decode.
- Defaults-only bench rerun stays in the baseline family everywhere
except the exact conditioning-cache win (stt 525–532, tts 225–355).
- `cargo clippy --workspace --all-targets -- -D warnings` + fmt clean
(pre-push hook battery green).
Measurement notes with per-lever logs: Eva's workspace,
`RESEARCH/HUDDLE_E2E_LATENCY_OPTIMIZATION_2026_08_12.md` +
`RESEARCH/HUDDLE_E2E_STT_FAKELLM_TTS_BASELINE_2026_08_12.md`.
## Suggested promotion order
1. Conditioning cache (in this PR, always-on, exact).
2. Streaming TTS at emit=12: bit-exact audio, biggest UX win — needs the
env-gate removed + barge-in soak + an ear pass on a real huddle.
3. Speculative decode with silence padding: near-free ~100–150 ms.
4. Threads: after min-spec A/B.
---------
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Signed-off-by: Mari <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Signed-off-by: tlongwell-block <tlongwell@block.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Co-authored-by: Mari <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Co-authored-by: tlongwell-block <tlongwell@block.xyz>
|
||
|
|
e0940927ff |
fix(channels): return complete member rosters (#5765)
## Summary - return complete channel rosters instead of truncating at 1,000 members - chunk `event_mentions` inserts inside one transaction so large kind `39002` snapshots remain discoverable by every `p` tag - add a targeted `buzz-admin reconcile-channels --channel <uuid>` force-republish path for stale discovery snapshots - cover a 1,501-member roster, 11,000-tag mention index, and kind `39002` tag construction past member 1,000 ## Why The relay builds NIP-29 discovery and several authorization decisions from `get_members()`, but that helper silently returned only the first 1,000 active members. Desktop then counted the truncated kind `39002` event, while late members could be rejected by roster-scanning member actions. Removing the roster cap exposes PostgreSQL's 65,535 bind-parameter ceiling in mention indexing, so the insert is chunked transactionally to preserve all-or-nothing indexing. The existing reconcilers only fill missing discovery events. The targeted admin option bypasses the separately known 1,000-channel reconciliation-list ceiling and replaces an existing channel snapshot using the configured production relay key. ## Attribution This supersedes and builds on #3166 by @LordMelkor. Thank you for identifying the roster boundary and contributing the original complete-roster and mention-index patch. The production roster/query changes and the two PostgreSQL regressions retain that work's shape; this PR rebases it onto current `main`, adds relay coverage, and adds the targeted repair operation requested for rollout. ## Validation Exact pushed head: `24d02e4f3824150ed84913c9d230e675502e5b12` - `cargo check -p buzz-db -p buzz-admin` - `cargo test -p buzz-db channel::tests::get_members_returns_full_roster_beyond_1000 -- --ignored --exact --nocapture` - `cargo test -p buzz-db feed::tests::insert_mentions_indexes_rosters_past_bind_parameter_cap -- --ignored --exact --nocapture` - `cargo test -p buzz-relay --lib handlers::side_effects::tests::group_members_snapshot_keeps_members_past_one_thousand -- --exact` - `cargo run -q -p buzz-admin -- reconcile-channels --help` - mandatory pre-push hook: branch-skew, desktop checks/typecheck/tests, mobile tests, Rust tests, and desktop Tauri checks all passed on the pushed head ## Rollout 1. Deploy the relay/backend build. 2. Run `buzz-admin reconcile-channels --channel <general-channel-uuid>` with `BUZZ_RELAY_PRIVATE_KEY` configured. 3. Verify the replacement kind `39002` roster count matches the active database membership count. No schema migration or desktop release is required. Fixes #3156 Supersedes #3166 --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
2693e0db1f |
Make workflow run history authoritative in Desktop (#5780)
## Summary - persist stable workflow run `error_code` values separately from human diagnostics - expose NIP-98 authenticated, channel-authorized run history and approval reads with stable keyset pagination - connect Desktop to those authoritative reads and return the relay-created run ID on trigger - show truthful loading, failure, and pending-trace states, and do not render approval actions from non-actionable stored hashes ## Validation - pre-push `branch-skew`, `desktop-typecheck`, `desktop-test`, `rust-tests`, `desktop-tauri-checks`, and `desktop-check` all passed on `a097dbe5f` - Desktop tests: 4,761 passed, 0 failed - `cargo check -p buzz-relay` - `git diff --check` ## Remaining gate This does not claim a relay-backed Playwright workflow journey. The browser relay bridge still routes workflow invokes through in-memory handlers; that production-shaped acceptance gate remains follow-up work before Workflows can leave preview. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz> |
||
|
|
4749bc7be3 |
feat(acp): report standard adapter usage (#4950)
## Why Claude Code and Codex expose standard ACP prompt-response usage, but Buzz only consumed Goose’s private cumulative usage notification. Their token use and Claude’s cumulative cost were therefore absent from NIP-AM metrics. ## What - Read per-turn `session/prompt` response usage for known Claude and Codex adapters - Publish Claude’s raw cumulative cost separately from per-turn tokens without changing the NIP-AM schema - Keep Goose usage exclusive and cover Claude/Codex wire serialization ## Risk Assessment Low-to-medium: changes best-effort observability only and does not affect prompt execution. The adapter-specific mappings preserve source semantics and omit unavailable fields. ## References - Validated with `cargo fmt --check`, `cargo test -p buzz-acp --no-run`, and full `cargo test -p buzz-acp` (678 passed at `652e373a` before merge-trailer amendment). Generated with Codex --------- Signed-off-by: Atish Patel <atish@squareup.com> Signed-off-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz> Co-authored-by: WorkerBeeGPT <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz.block.builderlab.xyz> Co-authored-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz> |
||
|
|
72d56e7bd3 |
fix(agent): raise output limit and allow 3 recoveries (#5475)
## Summary Raise the built-in output and recovery defaults so long-running agents have more room to finish useful work instead of terminating after repeated 32,768-token reasoning-only responses. - Raise `BUZZ_AGENT_MAX_OUTPUT_TOKENS` from 32,768 to 65,536 - Raise the finite output-truncation recovery allowance from 2 to 3 via `BUZZ_AGENT_MAX_TOKEN_RECOVERIES`; `0` still disables recovery - Strengthen the recovery prompt so the model stops prolonged reasoning, uses tools immediately, and builds scripts or artifacts in small verifiable steps - Preserve the safety invariant that incomplete truncated tool calls are discarded and never executed - Keep proactive handoff independently at 90% of `BUZZ_AGENT_MAX_CONTEXT_TOKENS` (180,000 tokens with the 200,000 default), regardless of the output allowance - Add request-loop and configuration regressions for exact-N recovery, disabled recovery, successful tool-first recovery, discarded truncated calls, and finite round bounds `BUZZ_AGENT_MAX_OUTPUT_TOKENS` remains an explicit per-agent deployment setting. Operators should configure it at or below the served model's output limit; this PR does not perform live provider capability discovery or automatic clamping. **Risk:** Medium — this increases the default request size and permits one additional recovery attempt by default. Recovery remains finite and bounded by `BUZZ_AGENT_MAX_ROUNDS`. Deployments whose served model rejects 65,536 output tokens must set a lower per-agent value. Current output limits - model - output token max - DeepSeek V4 Flash - 384,000 tokens - Qwen 3.8 (Max) - 131,072 tokens - GLM 5.2 - 131,072 tokens - GPT 5.6 - 128,000 tokens - Claude Opus 5 - 128,000 tokens - Gemini 3.6 Flash - 65,536 tokens - Kimi K3 (Moonshot)- 131,072 tokens ### Related issue None found. Originating benchmark analysis: `buzz://message?channel=c3252dd2-0142-4e01-88c7-a2183c3960a5&id=91e991aab5fd49094583c3937477f6c12db57a41d86edf7fd4745d0d57d10017` ### Testing - `cargo fmt --all -- --check` - `cargo test -p buzz-agent` — 595 passed, 0 failed, 0 ignored at `bd6de557b367850f50325bafdd3c046131942bef` - `cargo clippy -p buzz-agent --all-targets -- -D warnings` - Previously failing `cancelled_turn_with_usage_emits_notification_before_response` passed alone and in the full rerun - Push hooks passed: organization guard, branch skew, Rust tests, and Desktop Tauri checks ### Update — 2026-08-11 Per review feedback, the recovery default is 3. The OpenRouter live `/models` output-cap discovery, cache, request clamp, and related tests/documentation were removed. Per-agent output configuration is now the sole output-cap mechanism. Proactive handoff and its pre-usage byte fallback now depend only on 90% of `BUZZ_AGENT_MAX_CONTEXT_TOKENS`; with the 200,000 default, the handoff threshold is 180,000 regardless of `BUZZ_AGENT_MAX_OUTPUT_TOKENS`. Generated with Brainy Bumble ### Targeted validation — 2026-08-11 Ran the exact PR binary once on each of the 11 benchmark tasks causally affected by the previous 32,768-token ceiling, using OpenRouter with `deepseek/deepseek-v4-flash-0731` pinned to Fireworks and maximum reasoning effort. Relay-429 collection failures were excluded and rerun at concurrency 2. - **6/11 passed:** `circuit-fibsqrt`, `feal-linear-cryptanalysis`, `model-extraction-relu-logits`, `path-tracing`, `schemelike-metacircular-eval`, and `sqlite-db-truncate` - **5/11 reached the benchmark deadline:** `adaptive-rejection-sampler`, `dna-assembly`, `path-tracing-reverse`, `regex-chess`, and `write-compressor` - `regex-chess` reached exactly 65,536 output tokens, triggered one output-limit recovery, and then reached the deadline. This directly confirms that the larger ceiling and recovery path were active, but not that recovery guarantees completion. For context, ten of these tasks were 0/5 in the historical baseline; `sqlite-db-truncate`, the clean control, was 4/5. This is targeted one-attempt-per-task validation rather than a statistically powered comparison. The result should not be attributed solely to the recovery default of 3: this PR also raises the output ceiling and strengthens recovery behavior, and OpenRouter routing conditions may differ from the historical direct-Fireworks runs. Generated with Brainy Bumble --------- Signed-off-by: Atish Patel <atish@squareup.com> Signed-off-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz> Signed-off-by: Hardworking Honey <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz.block.builderlab.xyz> Co-authored-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz> Co-authored-by: Hardworking Honey <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz.block.builderlab.xyz> |
||
|
|
dc2dbfe0f5 |
feat(buzz-acp): idle re-sleep for woken lazy pools (#5682)
## What Adds an opt-in **idle re-sleep** for woken lazy ACP pools. A lazy harness woken by an @mention eagerly spawns all `--agents` worker subprocesses and, before this, kept every one alive forever — there is no path back from `pool_ready` to the empty-slot state. Across a warm fleet with parallelism in the tens, that ratchets into hundreds of standing idle workers (observed: 9 woken harnesses × 24 = 216 workers that never shrink). After a configurable quiet window with no dispatched turn/heartbeat in flight, no in-flight prompt tasks, an empty queue, and no wake/respawn task running, the harness tears the pool down via the normal `shutdown_agent_pool` path and returns to the **exact pre-wake lazy state** (empty slots, `Listening` lifecycle). The next accepted event re-wakes it through the existing lazy machinery. **No second pool lifecycle.** ## Why it's safe - **Race-safe with enqueue/wake by construction.** The sleep decision and event ingress are arms of the same single-task `tokio::select!`. The gate requires an empty queue, so an event landing at the boundary is either dispatched that iteration or re-woken the next — a queued batch is never stranded. - **Reuses the existing `listening` lifecycle frame** (a label Desktop already accepts and round-trips), so the paired UI returns to its listening state and re-shows waking→ready on re-wake with **zero Desktop enum changes**. - **Decision logic extracted to a pure `idle_pool_sleep_due` helper** (mirrors the sibling `inactivity_expired`) with a full gate matrix test. ## Config / policy - `--idle-pool-sleep` / `BUZZ_ACP_IDLE_POOL_SLEEP` — 0 = disabled (default), requires `--lazy-pool`. - Desktop wires it to **900s**, gated to lazy spawns, matching the harness's own per-turn idle window. Reserved key (desktop-owned lifetime policy) so user env can't disable it. ## Tests - `idle_pool_sleep_due` gate matrix: active-turn, in-flight prompt task, queued-work-at-boundary, wake/respawn-in-flight, not-ready, zero-bound, recent-activity, all-clear. - Config parse (`--idle-pool-sleep`), reserved-key membership. - `cargo test -p buzz-acp` → **761 passed, 0 failed** at base `63f961c7e`. Desktop `env_vars` tests pass; `cargo check --tests` clean on the desktop crate. > Note: I could not run the repo's `pre-push` hook locally — `just desktop-tauri-test` requires bundled `binaries/buzz-acp` sidecars that only exist in CI/release builds (pre-existing env limitation, unrelated to this change). Pushed with `--no-verify`; CI runs the authoritative gate. ## Scope Idle re-sleep only. Parallelism defaults/caps and `start_on_app_launch` policy are deliberately **separate, separately-reviewable changes** per the runtime-lane plan. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz> |