Skip to content

[upstream-sync] Merge block/buzz main (24 commits, f88cda9eb..de8a2741c) - #50

Merged
adrienlacombe merged 27 commits into
mainfrom
upstream-sync-20260821
Aug 21, 2026
Merged

[upstream-sync] Merge block/buzz main (24 commits, f88cda9eb..de8a2741c)#50
adrienlacombe merged 27 commits into
mainfrom
upstream-sync-20260821

Conversation

@adrienlacombe

@adrienlacombe adrienlacombe commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Upstream range

f88cda9eb..de8a2741c24 commits from block/buzz main, 357 files changed.

Merged with --no-ff. Merge commit has 2 parents and git rev-list --count upstream/main ^HEAD is 0.

What changed upstream

Workflows — a workflow editor (block#6248); send_message can now reply in-thread (block#6178).

Desktop — compact Buzz link metadata in messages (block#6252); context-aware Projects collaboration (block#6396) and selectable Projects workspaces (block#6368), which is what re-shaped the sidebar files this fork patches; Cmd+W closes the window (block#6314); lossless reconnect repair (block#6415); restored recent-channel sorting (block#6402); timeline stacking-context isolation from the focus drawer (block#6398); huddle speech-boundary preservation (block#6397); duplicate agent devices distinguished (block#6337).

Perfdiscover_acp_providers split into cheap and forced paths (block#6330); references resolved without directory scans (block#6328).

CLImessages thread --link now accepts a Buzz message link directly (block#6359). See Needs a human look.

Agents / LLM — cross-owner relay agent mentions fixed in owner-only builds (block#6338); unrequested public relay skills guarded (block#6394); thinking effort stamped on the call-completed log line (block#6424); Databricks alias-aware labels for 5 endpoints (block#6360); Buzz agent guidance simplified (block#6340).

Mobile — timeline and emoji interaction polish (block#6297), which deleted latest_message_button.dart.

Repo hygiene — pre-push lanes scoped to the branch merge-base diff (block#6423); a three-day dependency cooldown (block#6426); Buzz-native collaboration benchmarks (block#6264).

No migrations/ changes, no kind.rs changes, no release.yml changes in this range.

Conflicts

Four, all resolved:

File Resolution
AGENTS.md block#6359 rewrote the Deep Links section for the new --link form. Took upstream's instruction, kept the fork's dual-scheme framing.
desktop/src/app/AppShell.tsx Upstream wrapped <AppSidebar> in another provider and re-indented the whole prop block. Took upstream's structure and indentation wholesale, re-seated the fork's one onSelectMarkets line after onSelectProjects. The fork changes nothing else in this file.
desktop/src/features/sidebar/ui/AppSidebar.types.ts Took upstream's re-sorted import block and its new projectsOverviewActive prop; kept the fork's "markets" union member and onSelectMarkets.
desktop/src/features/messages/lib/composerMessageLinkNode.test.mjs Both sides added consts at the same spot. Kept both — upstream's PR_ID/PR_HREF and the fork's CANONICAL_CHANNEL_MESSAGE_HREF/MESSAGE_HREF_ATTR.

crates/buzz-relay/src/handlers/ingest.rs took 188/61 lines from upstream and auto-merged; the three KIND_SPONSOR_* hunks and their FORK-LOCAL comments are intact in both required_scope_for_kind and is_global_only_kind.

Two staged deletions are upstream's own (CreateWorkflowDialog.tsx via block#6248, latest_message_button.dart via block#6297) — confirmed absent from upstream/main.

Clean-merge review at fork patch sites

Upstream touched 8 patch sites; all fork markers verified present and still meaningful after the merge:

crates/buzz-core/src/lib.rs, crates/buzz-cli/src/lib.rs (the whole WalletCmd surface), crates/buzz-cli/TESTING.md, crates/buzz-relay/src/handlers/ingest.rs, desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx (the combined Inbox+Markets row survived intact), desktop/src/features/sidebar/ui/AppSidebar.tsx, desktop/src-tauri/src/lib.rs (deep-link argv filter + markets invoke handlers), desktop/src-tauri/src/commands/mod.rs.

Also checked, unchanged by this range but re-verified because a clean merge is not a correct merge:

  • assemble-manifest's gate — every lane its if: block names (setup, release, release-macos-unsigned, release-macos-x64, release-linux, release-windows) still has a jobs: entry. No dangling needs.<job>.result.
  • Sidecar liststauri.conf.json's externalBin matches every non-Windows lane's cargo build, macos-canary.yml included. No drift.
  • Both Cargo.locks still pin mesh-llm tag=v0.75.1 (3295c902), re-derived from the committed state with tomllib. The ci.yml desktop-lock patch stays a no-op and stays in place.
  • relay.rs is at 999 lines against the hard 1000 ceiling — unchanged by this sync, but still one line of slack. The documented next move is relocating pub mod allowlist; back to lib.rs (938 lines).

Verification

Every command run on this branch; all green.

Gate Result
cargo fmt --all --check pass
cargo fmt --manifest-path desktop/src-tauri/Cargo.toml --all --check pass
cargo clippy --workspace --all-targets -- -D warnings pass
cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets -- -D warnings pass
cargo metadata --locked (root + Tauri) pass
scripts/test-release-ref-contract.sh release ref contract passed
scripts/test-oss-desktop-promotion.sh OSS desktop promotion contract passed
scripts/test-mobile-worktree-overrides.sh all brand-rename checks passed
just test-unit 456 passed, 1 skipped
cargo nextest run -p buzz-paymaster 76 passed, 1 skipped
just desktop-typecheck (tsc --noEmit) pass
just desktop-test 5259 passed, 0 failed
just desktop-check (biome + px-text + pubkey-truncation) pass
just desktop-tauri-test pass
just desktop-build pass
just check (repo-wide lint + policy) pass
just file-size-check pass
dart format --set-exit-if-changed . 429 files, 0 changed
flutter analyze No issues found!
flutter test 1561 passed

Mobile gates ran under Hermit Dart 3.11.5 / Flutter 3.41.7.

CI: settled at 31 pass / 3 fail / 3 skipping — all three failures are upstream's

Full triage in the PR comments. In short:

Check Cause
Desktop Smoke E2E (4) tooltip-semantics.spec.ts:145 requires /Active \d+[mhdw] ago/ and gets "Active just now". Spec is byte-identical to upstream, arrived with de8a2741c (block#6252) at the tip of this range, and fails the same way on block/buzz main — same shard, shards 1–3 green there too. Line 150, five lines later, accepts just now; line 145 omits the alternation.
Desktop Aggregate gate — failed only at Check desktop jobs, a consequence of shard 4.
CodeQL 6 × js/incomplete-multi-character-sanitization, all html.replace(/<[^>]+>/g, "") in markdown.test.mjs stripping tags for an assertion. File byte-identical to upstream, arrived with block#6252. False positive.

Shard 4's own tally was 2 failed, 1 flaky, 281 passed; the flaky one is the known-flaky video-attachment.spec.ts:838 and passed on retry.

None of the four conflict resolutions in this PR touch tooltips, channel metadata, time formatting, or markdown sanitization.

CodeQL detail

CodeQL reports 6 new high-severity alerts, all js/incomplete-multi-character-sanitization in desktop/src/shared/ui/markdown.test.mjs (lines 1109, 1148, 1161, 1179, 1224, 1332), arriving with upstream block#6252.

Every hit is the same line shape:

const visibleText = html.replace(/<[^>]+>/g, "");

— stripping tags off renderToStaticMarkup output so the test can assert.equal on visible text. The rule is correct in general that one pass over <[^>]+> is defeatable by nested or malformed markup. It does not matter here: the input is the test's own fixture, and the result is only ever compared with an assertion — never rendered, stored, or trusted. git diff --stat upstream/main HEAD -- desktop/src/shared/ui/markdown.test.mjs is empty, so the file is byte-identical to upstream and no fork code is implicated. Zero alerts of this rule are open on main, which is why it reads as new.

This is the same shape as the useComposerLinkPreviews.test.mjs family triaged one sync earlier. A row was added to the AGENTS.md code-scanning table, along with the cheap discriminator for next time (is the file identical to upstream, and is the sink inside a test(...) block) so the next reader does not re-derive it from the query semantics.

Not dismissed — dismissal keys to an alert number and is a human action.

Needs a human look

1. Upstream's new --link flag rejects the scheme this fork emits. block#6359 added buzz messages thread --link, whose purpose is to take a link copied out of the desktop app. parse_message_link (crates/buzz-cli/src/links.rs:36) requires the scheme to be exactly buzz, while messageLink.ts in this fork emits bitcoinmarkets://. Verified by running it, not inferred:

$ buzz messages thread --link 'bitcoinmarkets://message?channel=<uuid>&id=<hex>'
{"error":"user_error","message":"expected a buzz://message link without credentials or a fragment","retryable":false}

Not fixed here — it is a behavioural change, not a merge resolution. But it is a narrower call than the still-open entity-link scheme decision: a --link argument is consumed locally and never travels inside message content, so accepting bitcoinmarkets:// costs no interop with upstream clients, and the "stops upstream clients rendering preview cards" objection does not apply. The fix is one || in that scheme check plus a test. Reasoning written into AGENTS.md.

2. AGENTS.md patch-table rows changed — this is why the PR is not self-merged. See the tripwire note below. The diff is docs(agents) only, no code:

3. Pre-existing, not from this sync: desktop/src/app/routeTree.gen.ts is out of sync with its generator. Running just desktop-build regenerates it with markets and projects swapped in ordering. Confirmed byte-identical to the same drift present on main before the merge, so it predates this range. Purely cosmetic ordering — typecheck, biome and the build all pass either way — so it was discarded rather than swept into a sync PR. Worth a standalone commit.

4. Pre-existing, not from this sync: crates/buzz-cli/TESTING.md rows 63–66 still document wallet message / publish / get / lookup, the NIP-SW subcommands removed with that feature. Stale in a fork-owned section of a fork-patched file. Left alone to keep this PR to upstream's changes plus resolutions.

No wire-format changes. No event-kind changes. No migrations.


Why this is not self-merged

Two of the sync task's tripwires fired:

  • Tripwire 3AGENTS.md patch-table rows changed. Worth noting which columns: the additions are new rows and the one edit is a rationale-column correction (AppSidebar.types.ts is upstream's file, not fork-added). No File or Change column changed, and the fork's patch set is byte-for-byte what it was before this sync — nothing was added to or removed from it. That is the low-severity half of this tripwire.
  • Tripwire 5 — a red check. Triaged above as a false positive in a file identical to upstream.

Neither is a reason the merge is wrong; they are reasons a human signs it off. Merging fires deploy-aws.yml at the live relay, which is why the gate is imperative rather than advisory.


Merge with a merge commit, not squash. A squash drops the second parent, leaves the merge base stale, and makes every later sync re-resolve these same conflicts from the same base — the failure 3ce7c8adc had to repair by hand.

klopez4212 and others added 26 commits August 20, 2026 09:45
## Summary

- Align mobile home avatars and share the same sticky-date and Latest
controls across channels and threads.
- Stabilize thread tail navigation and emoji category scrolling.
- Remove iOS glass compositing artifacts and match long-press blur
across action surfaces.

## Why

Threads and emoji selection used competing scroll and compositing paths,
causing rebounds, flicker, and inconsistent controls.

## Testing

- `just mobile-check`
- `flutter test` (1,529 tests)
- Signed Release installed, launched, and reviewed on a connected iPhone

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Signed-off-by: Watcher <bb7abfd757d0af7b66569d02ab9c0316b616f9d0c151ecf5b964344c462e7f8f@buzz.block.builderlab.xyz>
Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz>
Co-authored-by: Watcher <bb7abfd757d0af7b66569d02ab9c0316b616f9d0c151ecf5b964344c462e7f8f@buzz.block.builderlab.xyz>
## 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
block#6128
- The named-path regression case covers the issue being fixed by
block#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>
## Summary

Projects workspaces now support consistent selection across projects,
repositories, reviews, tasks, and channel results. A user can collect
related entities, see the active selection without losing their current
workspace, and pass a bounded, injection-safe representation to an agent
for discussion.

Detail views now keep repository source and availability explicit while
preserving contextual actions across local and remote workspaces.
Missing or inaccessible repositories produce a recoverable state instead
of leaving the workspace ambiguous.

This is part 2 of the Projects v6 stack. Parts 3 and 4 will add
context-aware collaboration and navigation/detail-page polish.

### Related issue

Related: block#6335

### Testing

- Full pre-push gate passed: file-size checks, Biome, TypeScript,
Desktop unit tests, Tauri checks, and Rust tests
- Project context safety suite: 9/9 passed, including bounded untrusted
overview data and hidden-context stripping
- Updated Playwright coverage exercises grouped selection,
selection-aware agent context, repository source states, and detail
actions

---------

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
## 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>
## Summary

- close the current Buzz window with `Cmd-W` on macOS
- keep Buzz Term's existing `Cmd-W` behavior when the terminal owns
input, where it closes the active terminal tab
- cover the close-window chord and its modifier/composition guards with
unit tests

## Why

Buzz removes macOS's native Close Window menu accelerator so the webview
can conditionally route `Cmd-W` to Buzz Term. That also left normal Buzz
mode without the standard close-window shortcut. The new app-level
bubble-phase handler restores window closing while allowing the
terminal's capture-phase handler to retain priority.

For the main window, the existing Rust close-request handler keeps the
webview alive and hides it to the tray, matching the titlebar close
button.

## Validation

- desktop unit tests: 5,093 passed
- focused close-window and terminal shortcut tests: 28 passed at
`71a2cce42013440e297c7b237d45b20e9638078d`
- desktop TypeScript: passed
- desktop checks: passed (pre-existing warnings only)
- workspace Rust clippy: passed
- Tauri Rust clippy/check: passed
- desktop build: passed
- mobile format/analyze: passed
- Tauri tests: 2,592 desktop tests and 26/27 terminal tests passed; the
existing `default_prog_child_observes_the_login_argv0` PTY fixture
repeatedly timed out waiting for `/bin/sh`, including when run alone,
and is unrelated to this TypeScript shortcut change

---------

Signed-off-by: Matt Kursmark <kursmark@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
## Summary

- distinguish same-name owned agents by management provenance: `managed
here` for Desktop-managed identities and `managed elsewhere` for
same-owner relay identities
- show provenance only when same-name suggestions collide, alongside
each identity's short npub
- preserve exact-pubkey selection and keep unique-agent autocomplete
unchanged
- add a composed mock-bridge regression covering relay owner
propagation, rendered labels, keyboard/pointer selection, and outbound
mention pubkeys

## Testing

- focused mention suggestion mapping and label tests
- composed Desktop E2E passes for both same-name identities and
exact-pubkey routing
- causal mutation verified: replacing the relay candidate's
`ownerPubkey` with `null` makes the composed E2E fail on the `managed
elsewhere` assertion
- pre-push Desktop checks: Biome, TypeScript, file-size ratchet, and
5,103 Desktop tests

## Manual test

With two same-name owned agents visible in a channel, type `@<name>`.
Duplicate rows identify the identities as `agent · managed here` and
`agent · managed elsewhere`, include distinct short npubs, and selecting
either routes the mention to that row's exact pubkey.

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary
- give inline project agents bounded visible-page and selection context
while reusing the shared message-thread presentation
- add contextual collaboration actions for discussing project entities
in related channels
- align project list metadata, context rails, and work-item
communication actions with the active workspace

This is Part 3 of the Projects v6 stack, following block#6368. Part 4
contains the remaining navigation and detail-page polish.

## Testing
- Desktop unit suite: 5,125/5,125 passed
- Projects smoke specs: 62/62 passed
- TypeScript, Biome, typography, pubkey, and differential file-size
checks passed
- full pre-push gate passed

## Post-Deploy Monitoring & Validation
- validate Projects overview/detail agent chat and discuss-in-channel
journeys in the first staging Desktop session
- healthy signals: context matches the active project/repository/work
item, messages remain in the chosen channel, and restored conversations
exclude unrelated DM history
- failure signals: stale or cross-project context, duplicate/missing
thread rows, or collaboration actions targeting the wrong channel;
mitigate by reverting this PR

Related: block#6335

---------

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
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>
…ints (block#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>
… test (block#6356)

## Summary

Fixes block#6249.


`key_backup::tests::generated_passphrase_respects_word_count_and_separator`
joined words with `-` and asserted the phrase splits back into exactly
`count` parts. The EFF short wordlist contains exactly one hyphenated
entry (`yo-yo`, line 1281 of 1296), so drawing it into either
hyphen-joined arm yields one extra part — a ~1-in-186 flake per full
suite run (`1 - (1 - 1/1296)^7 ≈ 0.539%`).

This switches the two hyphen arms to `|`, which cannot appear in the
wordlist — the exact guard the sibling test
`generated_passphrase_clamps_word_count` already documents and uses. The
space, dot, and empty-separator arms are untouched (no wordlist entry
contains a space or a dot), so the test still covers word count,
wordlist membership, and minimum length.

`generate_passphrase` itself is unchanged — a hyphenated word in a
hyphen-joined passphrase is not a product defect, only an ambiguity the
test's parsing could not handle.

## Verification

At `main` (196d62f), compiled the desktop test binary once and looped it
2000× per state:

| State | Failures / 2000 runs | Expected |
|---|---|---|
| old code | 15 | ~10.8 (P ≈ 0.539%) |
| fixed | 0 | 0 |

Full desktop Tauri suite (`cargo test --workspace` in
`desktop/src-tauri`): **2693 passed, 0 failed**.

Signed-off-by: Fizz <1f3b09af3c417274e5516bf95fadd3c118f35a31ae922257d550c69159ba931c@buzz.block.builderlab.xyz>
Co-authored-by: Fizz <1f3b09af3c417274e5516bf95fadd3c118f35a31ae922257d550c69159ba931c@buzz.block.builderlab.xyz>
## Problem and intent

Buzz huddles have been clipping speech into fragments: one live turn
arrived as the single letter “M,” and another ended mid-sentence. The
detector was making each frame decision independently, but the
surrounding endpointing policy had no onset confirmation or pre-roll,
used one threshold for both entering and leaving speech, and silently
discarded short segments.

This PR keeps Earshot 1.1.0 and fixes that policy around it. It is
intentionally model-independent so the separate Earshot/Silero bake-off
can evaluate detectors under the same segmentation behavior.

## What changed

One production file changed: `desktop/src-tauri/src/huddle/stt.rs`
(**+367/−79** versus `main`).

- Add a pure `VadEndpoint` state machine around Earshot probabilities.
- Preserve 256 ms (16 frames) of pre-roll before confirmed onset.
- Require three consecutive frames above 0.50 to enter speech.
- Use 0.35 to leave speech, preserving hysteresis-band audio.
- Retain 96 ms (6 frames) of hangover while keeping the existing 304 ms
silence-flush window.
- Make short-segment drops visible in logs instead of silent.
- Add boundary, onset, hysteresis, hangover, drop-path, and PTT policy
tests.
- Bind the thresholds to Earshot 1.1.0 in source. Earshot 1.2.x is
deliberately parked pending a matched-policy bake-off; block#6392 separately
prevents Renovate from silently crossing that boundary.

## Boundary behavior and known tradeoffs

A hard message boundary clears pre-roll. That prevents segment N audio
from reaching segment N+1, but a fast follow-up turn may receive less
than the full 256 ms onset window.

On the 121-clip corpus, **12 of 25 non-first segments had truncated
pre-roll, with 32 ms worst observed at the shipping constants**. This
result **holds at both 208 ms and 256 ms on this corpus**; it is not
independent of pre-roll. The observed follow-ups were bimodal—fast cases
clustered at 2–10 frames and the next case was 19—so no larger pre-roll
budget could reach those fast cases in this corpus.

If a future refactor makes pre-roll survive a boundary, segment N
reaches segment N+1 under the strict predicate:

```text
gap < VAD_PRE_ROLL_FRAMES - VAD_ONSET_FRAMES
```

At the shipped values that means gaps 0–12 leak and gap 13 is the first
clean case. Hangover and the silence-flush window do not enter this
bound because leakage rides the pre-roll deque. Marker-origin tests
exercise the real flush/reset and next-onset drain; a rolling-buffer
mutant leaks 3,328 samples and fails them.

Other known behavior:

- `voiced_frames` now counts frames above the 0.35 exit threshold once
speech begins, not only frames above 0.50. Corpus drop counts measured
under `> 0.50` counting must be re-baselined rather than compared
directly; the old “19 silent drops” figure is not a valid before/after
baseline.
- If PTT releases while the manually-open microphone flips or remains
on, there is no combined-transmit falling edge. The uninterrupted
utterance correctly closes on normal VAD timing instead of an edge
flush.
- The GUI-to-`push_audio_pcm` wiring is unchanged. The final acoustic
gate exercises the checked-in release-profile latency harness against
the real production `SttPipeline` and `TtsPipeline`; Tyler’s hand-test
covers the live GUI leg by design.

## Verification ledger

All gates attest exact head `2dad6bb0e2e2a0af115a9a06c26cf6183fedb633`.

- **Implementation:** complete pre-push gate green; remote SHA matched
local HEAD.
- **Exact-SHA verification:** `cargo fmt --check`; `cargo clippy
--all-targets -- -D warnings`; full `desktop/src-tauri` tests: **2,702
passed, 0 failed, 18 ignored**; HEAD re-confirmed unchanged afterward.
- **Mutation review:** engine region SHA-256
`3c161d8f6e4d22c02dff3c76498430964beda51a034d34651dbdb03e8ff69aa0`; 5/5
policy mutants killed, including deleted-clear and rolling-buffer
regressions. Direct gap sweep leaked at 0–12 and was clean from 13.
- **Release acoustic gate:** negative control deliberately overstated
the expected segment count and failed with exit 101. Soft/short onset
produced 1/1 segment (`“I'm happy.”`). Natural-pause fixture used two
exact 700 ms pauses (>304 ms) and produced the pre-registered 3/3 intact
segments at the scripted boundaries.
- **Release CPU beside Pocket TTS:** soft fixture median 32.1%, p95/max
56.8% (5 sparse samples); natural fixture median 14.55%, p95/max 17.2%
(8 sparse samples). These are scoped process samples from synthesis
start through append acceptance, not a general desktop CPU benchmark.

Local review receipts (not committed to the repository):

- `.scratch/vad-live-release-2dad6bb0e/`
- `RESEARCH/VAD_ARM_B_MUTATION_RIG_2026_08_20/`
- `RESEARCH/EARSHOT_1_1_0_TO_1_2_2_MEASUREMENT_2026_08_20.md`

## Hand-test focus

Before merge, exercise the two live shapes that originally failed:

1. Soft short openers such as “M” and “yes.”
2. Natural mid-sentence pauses.

Also try a reply immediately after the previous message commits; that is
intentionally the least-protected onset case because hard boundaries
clear pre-roll.

---

Authorship disclosure: this change was implemented and the PR opened by
**Wren**, Tyler’s Buzz agent, using Tyler’s GitHub identity after prior
disclosure and authorization in the originating Buzz thread. Tyler
remains the accountable human reviewer/operator.

---------

Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
**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 library with action
menu](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6248/workflow-library-actions-c5c3abc.png)

### Workflow editor — wide

![Wide workflow editor with step
details](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6248/workflow-editor-wide-c5c3abc.png)

### Workflow editor — narrow inspector overlay

![Narrow workflow editor with inspector
overlay](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6248/workflow-editor-narrow-c5c3abc.png)

### Workflow editor — active channel overlay

![Workflow editor portalled over the active
channel](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6248/workflow-editor-channel-overlay-c5c3abc.png)

## 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: block#231.

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
## 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>
## Summary

- keep `BUZZ_BUILD_AGENT_ACCESS_OWNER_ONLY` scoped to Desktop-managed
local start and provider deployment boundaries
- stop filtering independently operated relay agents by viewer/owner
equality in native discovery, autocomplete, and send-time revalidation
- preserve NIP-OA ownership verification, owner-authored `respond_to`
policy, shared-channel membership, and fail-closed send-time checks
- replace the packaged-build regression expectation with coverage for
cross-owner allowlisted and `respond_to=anyone` relay agents, including
the emitted agent `p` tag

Fixes block#6329.

## Why

The packaged 0.5.17 build reused its managed-runtime owner-only
capability in relay-agent mention admission. That silently hid correctly
configured shared agents owned by another operator, even when their
verified policy explicitly authorized the viewer. The capability is
intended to constrain runtimes Desktop starts or deploys, not external
relay agents.

## Validation

- `pnpm --dir desktop test` (5093 passed)
- `cargo test --manifest-path desktop/src-tauri/Cargo.toml
relay_directory --lib` (4 passed, 2 ignored real-relay tests)
- focused agent mention unit tests (35 passed)
- Desktop TypeScript/E2E build
- focused Playwright mention tests (3 passed)
- pre-commit formatting hooks
- pre-push branch-skew, file-size, Desktop check/typecheck/unit tests,
and Tauri checks
- `git diff --check`

---------

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

- keep the channel poll membership-only and fetch the open-channel
directory only from explicit discovery surfaces
- resolve unknown ID-bearing references through one bounded
`get_channel_details` lookup, while name references remain limited to
member channels plus a browse/search-warm directory cache
- fail closed for fetched private metadata and cover cold lookup,
warm/member hits, private visibility, and cached misses with mounted
hook contracts

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
Configure Renovate to wait three days before proposing newly released
dependencies.

The policy requires a registry publication timestamp and suppresses
branches until the release ages past the window. Renovate security-alert
updates continue to bypass minimum-release-age checks, so known fixes
are not delayed.

Checked with `renovate-config-validator --strict --no-global
renovate.json`, `jq`, and `git diff --check`.

Signed-off-by: Jordan Mecom <jm@squareup.com>
Lefthook selected pre-push lanes from the file set `git diff --name-only
HEAD @{push}` returns, which is overinclusive two different ways:

- **Rebase/merge poisoning.** `@{push}` is a two-dot diff against the
branch's last-pushed tip. After a rebase onto newer main — or the `git
merge origin/main` that `check-branch-skew.sh` itself instructs agents
to run — that diff includes everything `main` changed since the last
push, so `mobile-checks`, the `desktop-*` lanes, and `rust-tests` fire
on branches that never touched those trees (a recent relay-only push
discovered 266 files, 26 of them `mobile/`).
- **Brand-new branch in a worktree.** When `@{push}` doesn't resolve,
lefthook reads `$GIT_DIR/refs/remotes/origin/HEAD` — which doesn't exist
in a linked worktree's git-dir — then falls back to the
alphabetically-first `HEAD ->` remote. In a clone with a stale
third-party remote sorted before `origin`, that diffs against a frozen
tip and runs the full suite. All agent work happens in worktrees.

## Fix

Add `files: git diff --name-only origin/main...HEAD` to the six globbed
pre-push lanes (`rust-tests`, `desktop-check`, `desktop-typecheck`,
`desktop-test`, `desktop-tauri-checks`, `mobile-checks`). The three-dot
form is the branch's merge-base diff against `origin/main` — the same
base resolution `scripts/check-file-sizes-core.mjs` (`resolveBaseRef`)
and CI's dorny/paths-filter already use. A lane now fires only when this
branch actually changed a file it covers; `main`-side changes never
re-trigger it, and a new branch diffs against `origin/main` (empty diff
→ all globbed lanes skip) rather than a stale remote. `branch-skew` and
`file-size-check` stay unfiltered by design.

Also pin hook dispatch to the Hermit-managed lefthook. The generated
`.git/hooks/*` dispatchers prefer `$LEFTHOOK_BIN`, then a `lefthook` on
`PATH` — so a Homebrew lefthook shadowed the pinned 2.1.3. A `rc:
bin/.lefthookrc` (a lefthook config key `lefthook install` bakes into
every dispatcher) exports `LEFTHOOK_BIN` to `bin/lefthook`, so a push
runs the pinned version regardless of `PATH`. `just hooks` already runs
`lefthook install`, so re-running it picks up the `rc` line — no recipe
change needed.

`AGENTS.md` documents that lanes are scoped by merge-base diff and that
agent shells should lead `PATH` with `./bin` so pinned tools win.

## Verification

Reproduced both bugs in a scratch repo mirroring the worktree +
stale-remote topology, with lanes stubbed to echo on selection:

- **Bug 1:** relay-only branch, `git merge origin/main` (main carried
`mobile/` + `desktop/` changes). Without the fix, `desktop-check` and
`mobile-checks` fired; with it, three-dot diff = only the relay file,
all globbed lanes skip.
- **Bug 2:** brand-new branch, no upstream, in a linked worktree with an
alphabetically-first stale remote. Confirmed `@{push}` is `fatal`,
`refs/remotes/origin/HEAD` is absent from the worktree git-dir, and the
stale remote's `HEAD ->` sorts first. Without the fix it fired lanes off
the stale tip; with it, lanes scope to `origin/main` merge-base and
skip.
- **Positive controls:** a `mobile/**` commit still fires
`mobile-checks`; a `crates/**` commit still fires `rust-tests` +
`desktop-tauri-checks`.
- **Dispatch:** with Homebrew lefthook 2.1.10 on `PATH`, running the
real generated `.git/hooks/pre-push` under `LEFTHOOK_VERBOSE=1` shows it
sourcing `bin/.lefthookrc`, exporting `LEFTHOOK_BIN`, and running
`lefthook v2.1.3`.
- This PR's own push (a brand-new branch in a linked worktree — the bug
2 case) ran only `branch-skew` and `file-size-check`; no
mobile/desktop/rust lanes fired, since the branch touches only
`lefthook.yml`, `AGENTS.md`, and `bin/.lefthookrc`.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary

- repair exact-channel reconnect gaps from a source-coupled 1,865-second
floor (`900` seconds of relay future tolerance + `960` seconds of DB
created-at floor + `5` seconds of fence margin)
- page the existing authenticated `POST /query` bridge losslessly with
`(until, before_id)`, including dense timestamp boundaries and an
unbounded first page that does not trust the renderer clock
- keep the restored live REQ unchanged while deduplicating live/repair
overlap until both live EOSE and repair completion for the active
connection generation

## Scope and behavior

The wider repair applies only to live subscriptions containing the exact
channel-event kind set and one `#h`. Profile, read-state, global,
channel-less, and other subscription behavior is unchanged. Failed or
stale repairs retain their pinned lower bound for the next reconnect.

This intersects the replay path described in block#3215, but does not claim
to fix that issue. Desktop already has a bounded notification-side
event-ID guard; this PR adds transport dispatch dedupe for the
restored-live/repair overlap. Notification behavior should remain a
focused regression check while this wider repair window rolls out.

## Testing

- `cd desktop && pnpm typecheck`
- `cd desktop && pnpm biome check
src/shared/api/channelReconnectRepair.ts
src/shared/api/relayClientSession.ts src/shared/api/relayClientShared.ts
src/shared/api/relayClosedRecovery.ts
src/shared/api/relayReconnectReplay.ts
src/shared/api/relayReconnectReplay.test.mjs src/testing/e2eBridge.ts`
- `cd desktop && node --test
src/shared/api/relayReconnectReplay.test.mjs` (26 passed)
- `cargo test --manifest-path desktop/src-tauri/Cargo.toml
channel_reconnect_repair` (2 passed)
- pre-push hooks passed at `743283439eb6be7969e19951cbb6720c5e493a04`

---------

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

The "Jump to latest" pill (`z-50`) in the main timeline renders on top
of the focus thread drawer (`z-41`) because both share the same stacking
context. This wraps the timeline contents in an `isolate` div so the
pill's z-index can't escape above the drawer.

The diff is large but the actual change is two lines — one wrapper
`<div>`. The rest is biome re-indenting the children. The
`DropZoneOverlay` also moves inside the wrapper, so its top edge no
longer extends behind the header during drag-over.

### Repro

1. Open a channel with enough history to scroll up past the bottom
2. Scroll up so the "Jump to latest" pill appears
3. Open a thread in focus mode — the pill renders on top of the drawer

### Related issue
None found.

Before:
<img width="824" height="730" alt="Before"
src="https://github.com/user-attachments/assets/eae0eb88-bcef-4c2a-bb05-08d0af6f7f38"
/>

After:
<img width="1280" height="720" alt="after-drawer-hides-pill"
src="https://github.com/user-attachments/assets/0370ba71-efc5-478a-a8e0-aac902a66082"
/>

Signed-off-by: Matt Krueger <krueger.matt@ymail.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
## Summary

- batch desktop last-message queries into groups of 128 so workspaces
with larger channel counts stay within the relay's explicit-channel
limit
- propagate timestamp query failures instead of replacing the sidebar's
cached recency with all-null data
- advance channel recency from live message events so Recent ordering
updates without waiting for the next refresh

## Root cause

The desktop sent one explicit `#h` filter per channel in a single
`/query`. The relay rejects a request with more than 128 aggregate
explicit channel values. The desktop swallowed that rejection and
returned no timestamps, so Recent correctly fell back to A–Z for every
channel.

## Testing

- `cargo test --manifest-path desktop/src-tauri/Cargo.toml
last_message_filters_stay_within_relay_channel_cap --lib`
- `cd desktop && pnpm test` (5,132 passed)
- `cd desktop && pnpm exec biome check
src/features/channels/useLiveChannelUpdates.ts
src/features/channels/lib/channelRecency.ts
src/features/channels/lib/channelRecency.test.mjs
tests/e2e/channel-sort.spec.ts`
- `cd desktop && pnpm check:file-sizes`
- `cargo fmt --all -- --check`
- `cd desktop && pnpm build:e2e` followed by `pnpm exec playwright test
tests/e2e/channel-sort.spec.ts --project=smoke --workers=1` against a
dedicated static server (4 passed)

## Manual test

In a workspace with more than 128 channels, choose **Channels → Sort →
Recent**. Channels should order by latest message instead of A–Z. While
Recent is selected, a new message in a visible channel should move that
channel to the top immediately.

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz>
…hs (block#6330)

Splits the `discover_acp_providers` Tauri command into a cheap cached
path and an explicit forced re-discovery path.

The profiling flight-recorder identified `discover_acp_providers` as the
acute UI-freeze correlate: a 20–65s probe pipeline (PATH re-fetch, CLI
auth probes, process spawns) was running on routine renders like channel
switches, because hot surfaces remounted the wrapping query and
automatic staleness refetches re-ran the full pipeline. This is
complementary to the idle auto-reload (block#5588) and bounded-accumulator
(block#5596) work.

## Backend
- Added a `force` parameter to `discover_acp_providers` /
`discover_acp_runtimes_from`. The default (cheap) path serves cached
availability and auth statuses with zero process spawns; `force: true`
runs the full pipeline (cache clear, PATH re-fetch, CLI auth probes).
- Added a single-flight coalescer (`forced_single_flight`) so concurrent
forced runs share one probe instead of stacking.
- Extracted the auth-status cache and `resolve_auth_statuses` into
`discovery/auth_status_cache.rs`.

## Renderer
- Raised `staleTime` to 30 min; forced refresh flows only through an
explicit `fetchQuery` path (`refreshAcpRuntimes`), so automatic
staleness refetches can never re-run probes.
- Added `useAcpRuntimesQueryForced` for Settings and onboarding
surfaces; connect/install/save/delete mutations force-refresh in
`onSettled`.
- Switched `SetupStep`, `HarnessCatalogDialog`, and
`HarnessesSettingsPanel` to the forced hook.

## Structure
The touched files were at or over the grandfathered 1000-line file-size
ceiling, so the change is delivered as cohesive extractions rather than
in-place growth: `forced_single_flight.rs`, `auth_status_cache.rs`,
`discovery/tests/forced_discovery.rs`, `acpRuntimesQuery.ts`, and
`tauriAcpDiscovery.ts`. Dependencies are one-directional (`hooks.ts`
imports from `acpRuntimesQuery.ts` and re-exports the forced hook, so
consumer imports are unchanged; no import cycle).

## Tests
Added Rust tests pinning the cheap-vs-forced split: a probe is spawned
only when forced, the cheap path reuses the cache, and auth status is
`Unknown` before any probe runs.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
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>
**Category:** improvement
**User Impact:** Buzz links in messages now resolve into compact,
descriptive chips with predictable navigation, while tooltips use the
neutral secondary surface consistently across light and dark themes.

**Problem:** Raw Buzz-native links and opaque identifiers make message
context difficult to scan, while oversized previews disrupt the
conversation. Missing, delayed, or deleted metadata also needs a stable
presentation that does not mislead users or break navigation.

**Solution:** Keep message and issue chips metadata-independent: message
chips render only the channel name, while issue chips render only the
repository name—never fetched content/title or an event hash. Fetched
context remains available in each hover tooltip. Other native links
continue to resolve into concise inline labels, with muted secondary
fallbacks, distinct deleted states, and existing navigation paths
preserved. This also makes two explicit shared-tooltip behavior changes:

1. **Color:** shared and rich tooltips move from the primary color pair
to the neutral secondary semantic pair, including dedicated huddle-token
handling and matching rich-descendant foregrounds.
2. **Hover behavior:** tooltip roots set Radix
`disableHoverableContent`, and tooltip content is pointer-transparent,
so moving off the trigger dismisses the tooltip instead of letting the
pointer enter and keep the popover open.

Together these preserve readable contrast, authoritative project
context, and predictable hover dismissal.

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

**desktop/src/features/communities/useCommunityInit.ts**
Primes channel metadata needed to resolve native links promptly.

**desktop/src/features/messages/lib/messageLinkMetadata.test.mjs**
Covers message preview extraction, fallbacks, and deleted-message
semantics.

**desktop/src/features/messages/lib/messageLinkMetadata.ts**
Normalizes fetched message metadata into compact labels and status-aware
presentation.

**desktop/src/features/messages/ui/SentFromThreadLine.tsx**
Provides thread context to linked-message metadata resolution.

**desktop/src/shared/lib/useResolvedLinkPreviews.ts**
Exposes resolved preview state for native entity chips.

**desktop/src/shared/styles/globals/markdown.css**
Adds compact chip layout and semantic disabled-state styling.

**desktop/src/shared/styles/globals/theme.css**
Defines the semantic colors used by unavailable and deleted chips.

**desktop/src/shared/ui/markdown.test.mjs**
Exercises native-link rendering and provider-backed entity metadata
behavior.

**desktop/src/shared/ui/markdown.tsx**
Routes native markdown links through metadata-aware chip components
without changing navigation contracts.

**desktop/src/shared/ui/markdown/ChannelDeepLink.test.mjs**
Covers resolved, unresolved, private, archived, and forum channel
presentations.

**desktop/src/shared/ui/markdown/ChannelDeepLink.tsx**
Renders channel names and concise channel metadata inline with stable
fallbacks.

**desktop/src/shared/ui/markdown/MessageLinkPill.tsx**
Renders loading, ready, unavailable, DM, forum, and deleted message
states compactly.

**desktop/src/shared/ui/markdown/entityLinks.tsx**
Enriches repository, issue, and pull-request chips and uses
containing-project context in tooltips.

**desktop/src/shared/ui/markdown/types.ts**
Extends markdown runtime metadata required by native chip rendering.

**desktop/src/shared/ui/markdown/useMessageLinkMetadata.ts**
Fetches and classifies linked-message metadata while preserving
navigable fallbacks.

**desktop/src/testing/e2eBridge.ts**
Adds deterministic metadata controls for runtime visual and navigation
tests.

**desktop/tailwind.config.js**
Makes semantic disabled colors available to chip styles.

**desktop/tests/e2e/entity-link-recipient-cards.spec.ts**
Verifies entity labels, project-aware tooltips, delayed metadata, and
navigation behavior.

**desktop/tests/e2e/navigation.spec.ts**
Verifies channel and message chip states, metadata resolution, deletion
handling, and navigation.

**desktop/src/shared/ui/tooltip.tsx**
Migrates the shared tooltip surface from the primary color pair to the
semantic secondary pair. It also sets Radix `disableHoverableContent`
and makes tooltip content pointer-transparent so leaving the trigger
dismisses the tooltip predictably.

**desktop/src/features/agents/ui/RestartDiffBadge.tsx**
Aligns restart-diff tooltip values and supporting copy with the
secondary tooltip foreground.

**desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx**
Aligns rich team tooltip descriptions, persona chips, avatars, and names
with secondary tooltip semantics.

**desktop/src/features/projects/ui/ProjectAuthorIdentity.tsx**
Uses the matching secondary tooltip foreground for project author
metadata.

**desktop/src/features/projects/ui/ProjectCards.tsx**
Aligns repository-unavailable tooltip descriptions with the secondary
tooltip foreground.

**desktop/src/shared/styles/globals/utilities.css**
Makes huddle tooltips consume their dedicated palette directly without
misleading primary or secondary aliases.

**desktop/src/shared/styles/globals/tooltipSemantics.test.mjs**
Pins direct huddle-token consumption and rejects semantic alias drift.

**desktop/tests/e2e/tooltip-semantics.spec.ts**
Verifies simple and rich tooltip surfaces and descendant tokens in Buzz
light and Catppuccin Mocha dark themes.

**desktop/playwright.config.ts**
Registers tooltip semantic coverage in the desktop smoke suite.

</details>

## Reproduction steps

1. Run the desktop app and open a message containing Buzz links for a
channel, message, repository, issue, and pull request.
2. Confirm message links show only the channel name and issue links show
only the repository name inline, with no fetched content/title or event
hash; confirm other native links remain compact rather than rendering
raw identifiers or expanded previews.
3. Hover the chips and confirm the neutral secondary tooltip adds
concise context without duplicating the visible label.
4. Open each chip and confirm it follows the same channel, message,
repository, issue, or pull-request navigation as before.
5. Inspect links while metadata is loading, unavailable, or definitively
deleted and confirm the fallback remains compact, legible, and navigable
where appropriate.
6. In Buzz light and Catppuccin Mocha dark themes, compare a simple
channel-members tooltip and the rich Add agents team tooltip; both
should use secondary semantics with readable nested content.
7. Move the pointer from a trigger toward its tooltip; confirm the
tooltip dismisses rather than remaining open as hoverable content.

## Screenshots

The matrix below covers both the default light theme and Catppuccin
Mocha with an orange accent. Message/issue-chip rows affected by the
stable-label pivot are temporarily omitted until they can be recaptured
from the real built UI; the prior images showed the obsolete expanding
inline label.

| State | Default light | Dark · orange accent |
|---|---|---|
| Public stream channel tooltip | ![Public stream channel tooltip —
default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/01-public-stream-channel-tooltip.png)
| ![Public stream channel tooltip — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/01-public-stream-channel-tooltip.png)
|
| Private forum channel tooltip | ![Private forum channel tooltip —
default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/02-private-forum-channel-tooltip.png)
| ![Private forum channel tooltip — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/02-private-forum-channel-tooltip.png)
|
| Public forum channel tooltip | ![Public forum channel tooltip —
default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/03-forum-channel-tooltip.png)
| ![Public forum channel tooltip — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/03-forum-channel-tooltip.png)
|
| Channel references, including unresolved | ![Channel references,
including unresolved — default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/04-unresolved-channel-reference.png)
| ![Channel references, including unresolved — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/04-unresolved-channel-reference.png)
|
| Unknown channel permalink | ![Unknown channel permalink — default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/05-unknown-channel-permalink.png)
| ![Unknown channel permalink — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/05-unknown-channel-permalink.png)
|
| Archived channel tooltip | ![Archived channel tooltip — default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/06-archived-channel-tooltip.png)
| ![Archived channel tooltip — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/06-archived-channel-tooltip.png)
|
| Repository entity | ![Repository entity — default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/13-repository-entity.png)
| ![Repository entity — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/13-repository-entity.png)
|
| Pull request entity | ![Pull request entity — default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/light-default/14-pull-request-entity.png)
| ![Pull request entity — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/chip-metadata/dark-orange/14-pull-request-entity.png)
|
| Entity metadata fallback | ![Corrected entity metadata fallback
tooltip — default
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/17-entity-metadata-fallback-corrected-light.png)
| ![Corrected entity metadata fallback tooltip — dark orange
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6252/17-entity-metadata-fallback-corrected-dark-orange.png)
|



### Tooltip semantic surface spot check

| Buzz light | Catppuccin Mocha dark |
|---|---|
| ![Rich team tooltip on the secondary surface in Buzz
light](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6290/buzz-team-tooltip.png)
| ![Rich team tooltip on the secondary surface in Catppuccin Mocha
dark](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6290/catppuccin-mocha-team-tooltip.png)
|

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Conflicts resolved:

- AGENTS.md — upstream block#6359 rewrote the Deep Links section for the CLI's new
  `messages thread --link` form. Took upstream's instruction, kept the fork's
  dual-scheme framing (this fork emits `bitcoinmarkets://` and accepts both).
- desktop/src/app/AppShell.tsx — upstream re-indented the whole AppSidebar prop
  block. Took upstream's structure and re-seated the fork's
  `onSelectMarkets` line after `onSelectProjects`.
- desktop/src/features/sidebar/ui/AppSidebar.types.ts — took upstream's import
  ordering and its new `projectsOverviewActive` prop; kept the fork's
  "markets" union member and `onSelectMarkets`.
- desktop/src/features/messages/lib/composerMessageLinkNode.test.mjs — both
  sides added consts at the same spot. Kept both: upstream's PR_ID/PR_HREF and
  the fork's CANONICAL_CHANNEL_MESSAGE_HREF/MESSAGE_HREF_ATTR.

Signed-off-by: adrienlacombe <6303520+adrienlacombe@users.noreply.github.com>
… record the CLI --link scheme gap

Three AGENTS.md corrections the 2026-08-21 sync forced.

The AppSidebar row claimed `AppSidebar.types.ts` was fork-added. It is not:
upstream added it in block#4281 and the fork only inserts two lines. That mattered
this sync, because the file conflicted for the first time — upstream re-sorted
its imports and added `projectsOverviewActive` — and a reviewer following the
old row would have looked for a fork-owned file.

Two patch sites had no row at all, and both conflicted this sync:

- `desktop/src/app/AppShell.tsx` carries the two lines wiring the Markets nav
  callback. Upstream re-indented the whole AppSidebar prop block around them,
  so the conflict arrived with no documented intent to resolve against — the
  same failure #31's undocumented feature caused in the 2026-08-20 sync.
- `composerMessageLinkNode.test.mjs` carries the PR #32 scheme-const fix.
  Upstream appends entity-link fixtures to the same const block.

Finally, upstream block#6359's new `buzz messages thread --link` flag rejects the
scheme this fork emits: `parse_message_link` requires exactly `buzz`, so a
link copied from the desktop app is refused by the fork's own CLI. Verified by
running it. Recorded with the open entity-link decision, but flagged as the
narrower call — a --link argument never travels inside message content, so
accepting both schemes there costs no upstream interop.

Signed-off-by: adrienlacombe <6303520+adrienlacombe@users.noreply.github.com>
),
);

const visibleText = html.replace(/<[^>]+>/g, "");
const issueHtml = renderEntityChip(
`buzz://issue?id=${EVENT_HEX}&owner=${OWNER_HEX}&d=buzz-world`,
);
const issueText = issueHtml.replace(/<[^>]+>/g, "");
Comment on lines +1161 to +1163
const pullRequestText = renderEntityChip(
`buzz://pr?id=${EVENT_HEX}&owner=${OWNER_HEX}&d=buzz-world`,
).replace(/<[^>]+>/g, "");
relayOrigin: null,
}),
);
const visibleText = html.replace(/<[^>]+>/g, "");
),
);

const visibleText = html.replace(/<[^>]+>/g, "");
assert.match(html, />engineering</);
assert.match(html, /wrapping-inline-chip/);
assert.match(html, /inline-chip-leading-fragment[^>]*>e</);
assert.match(html.replace(/<[^>]+>/g, ""), /engineering/);
@adrienlacombe adrienlacombe added the needs-human Sync stopped on a tripwire; a human must review and merge label Aug 21, 2026
`js/incomplete-multi-character-sanitization` ×6 in
desktop/src/shared/ui/markdown.test.mjs turned CodeQL red on this sync PR. Every
hit is `html.replace(/<[^>]+>/g, "")` stripping tags off renderToStaticMarkup
output so the test can assert on visible text. The rule is correct that a single
pass over that pattern is defeatable by nested or malformed markup, and it does
not matter: the input is the test's own fixture and the result is never
rendered, stored or trusted. The file is byte-identical to upstream/main.

This is the same shape as the useComposerLinkPreviews.test.mjs row one sync
earlier, so the section now leads with the cheap discriminator — is the file
identical to upstream, and is the sink inside a test block — rather than leaving
the next reader to re-derive it from the query semantics.

Signed-off-by: adrienlacombe <6303520+adrienlacombe@users.noreply.github.com>
@adrienlacombe

Copy link
Copy Markdown
Owner Author

CodeQL triage — the red check is a false positive in upstream's own test file

CodeQL fails with 6 new high-severity alerts, all js/incomplete-multi-character-sanitization in desktop/src/shared/ui/markdown.test.mjs:1109,1148,1161,1179,1224,1332. They arrived with upstream block#6252.

Every one is this line shape:

const visibleText = html.replace(/<[^>]+>/g, "");

The query is right in the abstract — a single pass over <[^>]+> is defeatable by nested or malformed markup. It is irrelevant here on three counts:

  1. The input is the test's own fixture. html is renderToStaticMarkup(...) output built inside the test from literal props, not untrusted content.
  2. The output is never rendered, stored, or trusted. It only reaches assert.equal / assert.doesNotMatch. There is no sink and no trust boundary.
  3. No fork code is implicated. git diff --stat upstream/main HEAD -- desktop/src/shared/ui/markdown.test.mjs is empty — byte-identical to upstream.

Zero alerts of this rule are open on main, which is why they register as new rather than inherited.

This is the second sync running with this shape — the js/incomplete-url-substring-sanitization ×6 family in useComposerLinkPreviews.test.mjs was the same story on 2026-08-16. AGENTS.md's code-scanning table now carries a row for this one plus the cheap discriminator up front: is the flagged file identical to upstream/main, and is the sink inside a test(...) block? That answers most of these without reading the query.

Not dismissed — dismissal keys to an alert number and is a human action. Alerts 69–74.

@adrienlacombe

Copy link
Copy Markdown
Owner Author

CI settled: 31 pass / 3 fail / 3 skipping — and all three failures are upstream's, not this merge's

Desktop Smoke E2E (4) + Desktop — upstream's own breakage, reproducible on block/buzz main

Shard 4 reports 2 failed, 1 flaky, 281 passed. The two failures are the same spec in both theme variants:

[smoke] › tests/e2e/tooltip-semantics.spec.ts:109:3
  › link tooltip supporting text uses the muted secondary foreground — buzz
  › ... — catppuccin-mocha

Locator: getByRole('tooltip').locator('[data-buzz-tooltip-metadata-type=""]')
Expected pattern: /Public channel · Active \d+[mhdw] ago/
Received string:  "Public channel · Active just now"

This is a defect in upstream's own test, and the adjacent assertion proves it. Five lines apart in the same test:

Line Assertion Accepts "just now"?
:145 /Public channel · Active \d+[mhdw] ago/ no
:150 /#general · .+ · (just now|\d+[mhdw] ago)/ yes

The mock channel's activity timestamp is close enough to now that the formatter returns "just now", which the first pattern cannot match. The tolerant alternation was written for the message-link tooltip and omitted for the channel one.

Three facts place this outside the fork:

  1. git diff --stat upstream/main HEAD -- desktop/tests/e2e/tooltip-semantics.spec.ts is empty — byte-identical to upstream.
  2. The spec arrived with de8a2741c (feat(desktop-messages): show compact Buzz link metadata block/buzz#6252), the tip of this range. It does not exist on this fork's main, so there is no "does it also fail on main" to check — it fails on the commit that introduced it.
  3. Upstream's CI fails identically. On block/buzz at de8a2741c: Desktop Smoke E2E (4): failure, Desktop: failure, shards 1–3 green. Same shard, same shape. de8a2741c is still upstream's tip, so no fix has landed yet.

None of this PR's four conflict resolutions touch tooltips, channel metadata, or time formatting.

Desktop failed only at its Check desktop jobs step — it is the aggregate gate over the shards, so it is a consequence of shard 4 rather than an independent failure.

The third result, video-attachment.spec.ts:838 › inline video hover reveals a timeline without a second play control, counted flaky (passed on retry) and is a known-flaky spec in this repo.

What to do about it

Not patched here, deliberately. A one-word regex fix in an upstream file is permanent conflict surface for a bug that belongs in block/buzz — and upstream will almost certainly land it themselves, at which point the next sync goes green with no fork patch to carry or remember to delete. The options, in preference order:

  1. Wait for upstream. Costs nothing; the fix is trivial and their own CI is red, so they are already looking at it.
  2. Send the one-liner to block/buzz — add (just now|…) to line 145. Removes the divergence permanently, and it is their bug.
  3. Merge this PR with the red check, accepting a known-upstream E2E failure on main. Reasonable given the diagnosis, but it does leave main red until upstream fixes it.

Cutting the sync one commit short of de8a2741c would dodge it, but it splits the range and leaves the merge base short of upstream's tip — not worth it for a test-only failure with a known cause.

@adrienlacombe
adrienlacombe merged commit e0364d7 into main Aug 21, 2026
34 of 37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human Sync stopped on a tripwire; a human must review and merge upstream-sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.