Skip to content

[upstream-sync] Merge block/buzz 93114c9c6..f88cda9eb (15 commits) - #49

Merged
adrienlacombe merged 18 commits into
mainfrom
upstream-sync-20260820
Aug 20, 2026
Merged

[upstream-sync] Merge block/buzz 93114c9c6..f88cda9eb (15 commits)#49
adrienlacombe merged 18 commits into
mainfrom
upstream-sync-20260820

Conversation

@adrienlacombe

@adrienlacombe adrienlacombe commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Upstream sync — 15 commits (93114c9c6..f88cda9eb)

Merged block/buzz@f88cda9eb into main with a real merge commit (two parents,
git rev-list --count upstream/main ^HEAD = 0).

What changed upstream

Desktop

MobileUnify mobile channel details (block#6113), chore: serialize mobile pre-push checks (block#6322)

Agent surfacefix(buzz-acp): loosen workspace-scan guardrail to allow named paths (block#6261),
fix(buzz-dev-mcp): expand leading ~ in read_file/str_replace paths (block#6271),
Revert "fix(acp): gate relay-signed workflow messages on their attributed author" (block#6311)

Mediafix(media): accept portrait video resolutions (block#6058)

No changes under migrations/, crates/buzz-core/src/kind.rs,
crates/buzz-relay/src/handlers/ingest.rs, or .github/.

Conflicts

File Resolution
desktop/src-tauri/src/lib.rs Took upstream's ptt_shortcut::install(builder) over the fork's older #[cfg(not(test))] builder.plugin(global_shortcut_plugin()). The fork's only patch in this file — the deep_link::is_supported_deep_link argv filter — is untouched and verified present.
desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx Both sides changed different things. Upstream block#6003 wrapped the header in a fragment, appended <SidebarProjectsSection />, renamed FolderGit2Folders and re-indented every line. The fork (#31) has Inbox and Markets sharing one height-stable row. Kept both: upstream's structure and indentation, with the fork's combined row re-seated where upstream's plain Inbox item was, and ChartNoAxesCombined added alongside upstream's Folders import.

Two clean-merge defects (neither produced a conflict marker)

1. Duplicated PTT relocation — fixed in 9c44b68. To shed 92 lines from
lib.rs under the file-size ratchet, the fork's #31 moved the global-shortcut
plugin construction into ptt_shortcut.rs as global_shortcut_plugin().
Upstream block#6024 independently made the same relocation into the same file as
install(). The two landed in different regions, so git auto-merged and kept
both copies; lib.rs calls only install(), leaving the fork's copy
unreferenced. Caught by tauri clippy as function global_shortcut_plugin is never used. Verified upstream's install() is a faithful superset (semantic
diff of the handler bodies is empty — only the wrapper signature, rustfmt
wrapping, and an added cfg(test) no-op arm differ), so ptt_shortcut.rs is
now byte-identical to upstream/main.

2. relay.rs file-size ratchet, 996 → 1003 against a hard limit of 1000.
Fixed by condensing the fork's two comment blocks from 5 lines to 2 → 999, per
AGENTS.md (do not split or reorganise upstream's relay.rs to make room). This
spends the last of the comment slack; see the AGENTS.md row for what to do next
time.

Verification

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 (after 9c44b68; failed before it)
cargo metadata --locked (root + tauri) pass — both lockfiles resolve
just file-size-check pass (failed before the relay.rs fix)
scripts/test-release-ref-contract.sh pass — release ref contract passed
scripts/test-mobile-worktree-overrides.sh pass
scripts/test-oss-desktop-promotion.sh pass
just test-unit pass — 1,483 tests across 8 suites
pnpm test (desktop) pass — 5,109 tests
pnpm exec tsc --noEmit (desktop) pass
pnpm check:px-text, check:pubkey-truncation pass
dart format --set-exit-if-changed . pass — 425 files, 0 changed
flutter analyze pass — No issues found!
flutter test pass — 1,553 tests

The three contract scripts were run in a clean clone; test-release-ref-contract.sh
copies target/ (~55 GB) when run from the working checkout.

CI

32 pass, 0 fail, 3 skip (Web, CodeQL, and the push-gateway publish — all
path- or event-gated). Notably green: Desktop Core (carries the
AppSidebarPinnedHeader.tsx resolution), all four Desktop Smoke E2E shards,
Desktop E2E Integration and Desktop E2E Relay, Mobile, and
Windows Rust (x86_64-pc-windows-msvc) — that last one matters because the
function deleted in 9c44b68 existed to keep the global-shortcut plugin out of
the Windows lib-test binary (STATUS_ENTRYPOINT_NOT_FOUND); upstream's
install() preserves that with a cfg(test) no-op arm, and this lane confirms it.

Fork-local patch sites — all verified intact

relay.rs (allowlist mod + default relay), native_websocket.rs (allowlist call),
deep_link.rs (DEEP_LINK_SCHEME, dual-scheme acceptance), lib.rs (argv filter),
tauri.conf.json (brand, identifier, scheme), relay/allowlist.rs. Upstream
touched the first four; markers survived and still make sense in their new
surroundings. release.yml was not touched by this range.

Needs a human look

  • Tripwire 3 fired — this is why the PR is not merged. A fork-local patch was
    deleted (feat(markets): Bitcoin difficulty betting (hidden L2, Lightning fund only) #31's PTT relocation, superseded by upstream perf(desktop): move five hot renderer paths from JS into Rust block/buzz#6024) and the AGENTS.md
    patch table changed. The table edits are one rationale-column update
    (relay.rs ratchet history) plus three new rows — the File/Change columns
    grew, which is the higher-severity kind. The new rows document the Bitcoin
    markets feature (feat(markets): Bitcoin difficulty betting (hidden L2, Lightning fund only) #31), which had been fork-local for eleven syncs with no row
    at all
    ; that gap is what made this sync's AppSidebarPinnedHeader.tsx
    conflict expensive to resolve.
  • AppSidebarPinnedHeader.tsx is a visual change worth eyeballing. The fork's
    combined Inbox+Markets row now sits inside upstream's new fragment, above
    upstream's new <SidebarProjectsSection />. AGENTS.md records that packing
    Markets as its own item previously broke virtualization.spec.ts "06", so that
    spec was run locally against this branch — 22 passed, including
    06 — custom-section dnd reorder commits under content-visibility, alongside
    sidebar-snapshot.spec.ts and sidebar-more-unread-overlap.spec.ts. The
    layout is therefore verified, not just type-checked; what remains is a
    subjective look at where Markets now sits relative to the new Projects
    section.
  • One upstream commit has no DCO trailer: 08eb46ef3 (Revert "fix(acp): …",
    Revert "fix(acp): gate relay-signed workflow messages on their attributed author" block/buzz#6311). It is upstream's own commit; signing it off would mean rewriting
    upstream history and destroying the merge parent, so it was left alone. This is
    informational only — no DCO check runs in this fork (there is no dco workflow
    and PR [upstream-sync] Merge block/buzz f8692fa9b..93114c9c6 (26 commits) #48 had none either); the DCO gate lives on block/buzz. Both commits
    authored here are signed off.
  • No wire-format changes. No migrations. No event-kind changes.

Merge with a merge commit, not squash — a squash drops the second parent and
leaves the merge base stale, which is the entire point of this job.

thomaspblock and others added 18 commits August 19, 2026 12:50
## Summary
- Makes Projects repository-aware across browsing, branch and tag
selection, local and remote source management, work items, commits, and
contextual repository actions.
- Refines project details into consistent single-column reading surfaces
with a resizable, section-aware context pod, while keeping project
conversations available in the attached chat panel.
- Adds persistent sidebar project navigation, direct entity links,
repository discussion channels, contributor identity matching, and
consistent loading and activity presentation.
- Keeps branch-specific controls on code-oriented sections while Tasks,
Reviews, and Channels remain repository-scoped, reducing misleading
context and actions.

Replacement for block#5981 with an identical final tree flattened into one
signed-off commit because the required DCO check suite remained stalled.

### Related issue
N/A

### Testing
- [x] Desktop pre-push checks, TypeScript typecheck, and unit tests
- [x] E2E production build with `pnpm build:e2e`
- [x] Focused Playwright smoke coverage for project workspace, task,
review, commit, sidebar, and contextual-panel behavior
- [ ] Attach final before/after screenshots after design review

---------

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Signed-off-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz>
Co-authored-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz>
…k#6306)

## Summary
- Replaces the drawer panel icon's CSS `translateX` slide with a
`motion/react` width + corner-radius morph, so the icon reads as the
panel opening rather than the glyph drifting sideways.
- Honors `prefers-reduced-motion` via `useReducedMotion` (no animation
for users who opt out).

Follow-up polish to the projects workspaces work that just landed in
block#6003 — the slide animation shipped there was the wrong visual.

## Test plan
- [x] Desktop unit tests and typecheck pass with this file at this
content (validated as part of the projects-v6 branch validation)
- [ ] Visual check: open/close the right drawer and confirm the icon
morphs in place

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

- combine channel identity, actions, member previews, and lifecycle
controls in one navigable mobile details page
- add compact member management and desktop-aligned channel editing
- smooth the profile entrance transition and standardize settings-group
spacing
- align Huddle lifecycle rows with regular message author and body
spacing

## Testing

- `just mobile-check`
- `just mobile-test` — 1,477 tests
- Pixel 10 debug build, install, and interaction review

## Snapshots

### Channel header

![Channel header with channel name and member
count](https://raw.githubusercontent.com/block/buzz/c61c64a8db0f69fc71e0ac344d64384a85ad02b5/pr-6113--01-channel-header.png)

### Combined channel details

![Combined channel details with actions and member
preview](https://raw.githubusercontent.com/block/buzz/c61c64a8db0f69fc71e0ac344d64384a85ad02b5/pr-6113--02-channel-details-members.png)

### Manage channel

![Manage channel editing
controls](https://raw.githubusercontent.com/block/buzz/c61c64a8db0f69fc71e0ac344d64384a85ad02b5/pr-6113--03-manage-channel.png)

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
)

🤖

## Summary

Desktop's `#`/Tab channel autocomplete offered archived channels as if
they were valid destinations. Stale channels competed with active ones
in the picker, including a confusing uppercase `STREAM` entry — the
badge of an archived stream channel leaking into the suggestion list.
After this change, autocomplete suggests only active, non-DM channels.
Links to archived channels in old messages still resolve normally.

## Demo

Before/after, real archive flow:

![Demo — archived channel disappears from #
autocomplete](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/6156/demo-archive-autocomplete.gif)

## What changed

`useChannelLinks.ts` builds the autocomplete suggestion list from the
raw relay channel list and previously excluded only DMs, so archived
channels slipped through. The fix filters `archivedAt === null` while
generating suggestions only — the shared navigation list stays
unfiltered, so resolving historical links to archived channels is
unchanged.

- `desktop/src/features/messages/lib/useChannelLinks.ts` — suggestion
selection centralized in `selectChannelSuggestions`; excludes archived
channels and DMs; keeps the existing eight-result limit.
- `desktop/src/features/messages/lib/useChannelLinks.test.mjs` — new
tests: archived stream/forum excluded, active ones included, DMs
excluded, mixed lists.

## Validation

At exact head `5a963b8407bd48f40d992e026d0a841d2445647f`: `pnpm check`,
`pnpm typecheck`, and the full Desktop unit suite (4,960 passed). Smoke
E2E run compared against exact base `f956e6fe`: the only failures are
two pre-existing failures that reproduce on base and four documented
pass-on-retry flakes — none touches the autocomplete surface.

Signed-off-by: loganj <loganj@squareup.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
## 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>
…6024)

## Summary

Moves five hot renderer paths out of JavaScript and into Rust, targeting
main-thread CPU on the desktop app: wakeups, IPC round trips, and
per-frame JS work during reconnect storms, catch-up, and archive sync.

Five commits, reviewed and ratified sequentially (each round included
independent re-review, mutation-tested coverage, and full-suite gates at
pinned heads):

| Commit | Change |
|---|---|
| `9b5a82ab9` | Batch inbound relay frames in the native websocket
plugin: N run-loop wakeups collapse into 1 per 8ms window, byte-bounded
so no batch crosses the Tauri direct-execute threshold onto the slow
fetch path. NIP-42 AUTH bypasses the window. |
| `8b047a35e` | Move the local-archive subscription into Rust: the
renderer no longer sees archived frames at all. Shared
`native_relay_client` (one authenticated socket per relay+pubkey,
declarative subscription reconciliation, CLOSED-driven retry with
write-time eviction), lease/epoch lifecycle ownership so delayed IPC
cannot resurrect a stopped task. |
| `609962b49` | Move persona catalog fetching into Rust: signature
verification (~473us/event, ~0.24s per 500-event page formerly on the
webview thread) now runs under `spawn_blocking`; one projected DTO
crosses IPC. |
| `38afea0a0` | Batch unread catch-up behind one IPC call: N per-channel
REQs/awaits become one command with global two-pass classification,
making the completion-order race unrepresentable. Both IPC DTO surfaces
pinned by wire tests asserting serde output against the TS contract. |
| `b955f528b` | Move observed unread state into native SQLite: scoped
WAL DB with a sequence/revision protocol, transactional localStorage
migration, epoch-tagged rebuild detection. Membership no longer
serializes five capped arrays per catch-up: measured 327.4 KiB before,
0.2 KiB after at the same populated 5x1000 fixture. |

Full design rationale, failure matrices, and verification detail are in
the individual commit messages.

### Known behavior notes

- **Native migration is a one-way door per profile.** A successful
native open sets `migration_complete` in the first open's transaction
and the renderer then deletes the legacy localStorage key
(`useObservedUnreadPersistence.ts:290-294`,
`observed_unread.rs:420-442`). The one-release JS fallback remains for
native open *failure*, but a profile that has completed migration cannot
return to the JS store. Relevant if a rollback path is ever needed.
- **The membership seed is built unconditionally at startup**
(`useUnreadChannels.ts:245-250`), before any `isNative()` check. It is
ref-cached and small, but it is a startup cost the baseline does not
pay, including on the fallback path.

### Related issue

None found. Work originated and was reviewed in the Buzz
`buzz-gui-performance` channel.

### Testing

**Current head is `9a8128ba4d2d695113505162be388fcec62cda0a`. For
receipts at that head, see [Round-3
receipts](#round-3--final-head-9a8128ba4) at the bottom; the block
immediately below is the historical record for the original five-commit
pack and its counts are superseded.**

The receipts in this subsection are at exact pack head
`b955f528b6dfa34db089b21cc718fba0f9284d6f` (tree oid `dcc711a66`),
verified by three independent instruments (implementer, reviewer,
gatekeeper), with `git rev-parse HEAD` confirmed in the same shell as
each run:

- `cargo test --lib` (full package): **2511 passed / 0 failed** (+7 +3
in the other targets)
- `pnpm test`: **4920 / 4920 across 74 suites**
- `cargo fmt --check`, `clippy --all-targets -D warnings`, `tsc
--noEmit`, `pnpm check` (including the file-size ratchet): all clean
- Mutation controls on the final review round: badge-lane controls A and
B each turn exactly one test red; seed-guard (M4) and
anchor-monotonicity (M5) mutants each killed by exactly their own
witness; restored bytes fully green
- The observed-unread native path is exercised by an in-tree
`__TAURI_INTERNALS__` protocol rig (entry, replay, gap, rebuild,
markers, membership, badge lane), added after review found the original
suite never left the localStorage fallback

**Open gates, stated plainly (status as of head `9a8128ba4`):**

- **Paired CPU benchmark still not run.** The three-arm design (baseline
`78cbffeb6` vs pack vs a null arm with native forced off, fresh profile
per arm) was instrument-reviewed but never executed. The IPC payload
reduction above is measured; end-to-end CPU improvement is predicted,
not measured. This gate is unchanged since `b955f528b`.
- **Live-local gate at the final head: GREEN at `9a8128ba4`.** A real
native Tauri live-local pass per `TESTING.md` was run at this exact
head. Receipts:
`.scratch/pr6024-final-9a8128ba4d2d695113505162be388fcec62cda0a/`,
`MANIFEST.txt` `start_utc=2026-08-17T21:58:07Z`, `FINAL_ATTESTATION.txt`
`end_utc=2026-08-17T22:20:47Z` — both after the commit's `21:32:51Z`,
and the build worktree's HEAD is `9a8128ba4` with a clean tree. **I
opened these files rather than relaying the summary**, and recomputed 6
SHA256SUMS entries against the sealed manifest (6/6 match). From the raw
artifacts: historical catch-up persisted 10 seeded events into the
native `observed-unread.db`; a realtime event advanced it 10→11; the
relay was killed and restarted with Desktop up and an event sent after
recovery advanced it 11→12 within 2 polling ticks; Desktop was then
stopped, an event sent offline, and after relaunch with the appdir
preserved catch-up advanced it 12→13. `relay.log` shows 2 app WebSocket
connections, the restart-close signal, and fresh connections after each
recovery — the app-attributable socket evidence the previous round
lacked. The preserved appdir database is still on disk and `select
count(*) from observed_events` returns **13**. The ignored real-relay
archive wire test passed **1/1** at this head. This exercises the
`lib.rs` / `e2eBridge.ts` merge recombination under a real app and
blocker 2's failure-mode family (relay killed mid-session, app killed
with offline traffic in flight, sync did not go dead). It does **not**
directly force the precise overlapping B/C archive-ownership timing
window through the GUI; that invariant is pinned by the compiled
`drop(owner)` mutation proof below, and is deliberately not relabelled
as live race evidence. **Retraction retained for the record:** an
earlier revision of this description said live-local was GREEN at
`9a8128ba4` while citing a run executed against `f079d0914`, the pre-fix
head, roughly three hours before `9a8128ba4` existed. That claim was
false and was corrected before this run existed. The GREEN above rests
on the new receipts only.
- **Workspace-wide `cargo test --workspace` is red on `main`, not on
this PR.** The exact-head live run's workspace logs contain honest
`rc=101` results. All are in crates this PR does not touch: `git diff
origin/main...9a8128b` is **empty** for `crates/git-sign-nostr`,
`crates/buzz-relay`, and `crates/buzz-pair-relay`.
`git-sign-nostr::tests::test_parse_envelope_rejects_invalid_oa_pubkey`
fails **deterministically on clean `main`** — reproduced independently
in fresh worktrees at merge-base `a282e0643` and at current tip
`7f61cf431` (55 passed / 1 failed, same assertion at `lib.rs:2136`).
Root cause: `nostr` 0.36→0.44 changed `PublicKey::from_hex` from a
curve-point parse to a plain hex decode, so an all-zeros (off-curve) key
now parses; measured with a standalone probe, `from_hex("0"*64).is_ok()`
is `false` on 0.36.0 and `true` on 0.44.7. Filed as block#6175,
with the observation that nothing in CI runs this crate's tests (`just
test-unit` enumerates packages and omits it; `server-cross-compile`
compiles it and runs nothing). The mesh-demo and pair-relay failures in
the same logs are timing flakes in equally untouched crates. **None of
these gate this PR.**
- **Full smoke + integration: GREEN at `9a8128ba4`.** CI run 32072147445
is `success` with every job green — all 4 Desktop Smoke E2E shards, both
Desktop E2E Integration shards, Desktop Core, Desktop Build (macOS),
Desktop E2E Relay, Rust Lint, Windows Rust, DCO, Dead Token Reference
Guard. Attempt 1 was cancelled by an infrastructure hang, not by this
code: both integration shards stalled 19m37s inside `Install Playwright
system dependencies` (an apt install that runs before the e2e bundle is
built or the relay starts), so no test executed and all downstream steps
were skipped. The same step took 16s at `9128b9389` and 12s/18s on
attempt 2 of these same bytes. Attempt 2 was a job rerun, not a push;
the head is unchanged.

---
Opened by Eva (agent) on Tyler's behalf at his request.


---

## Review round 2 — head `9128b9389`

Carl's review
[4953970089](block#6024 (review))
raised two blockers against `f079d0914`. Both were confirmed real at the
source by a second reader and both are fixed in `9128b9389`.

**Blocker 1 — recovery dropped the mutation whose ingest failed.** Every
captured mutation now gets one retry after `reopen()` refreshes
sequence/revision, which is safe because ingest is idempotent (events
upsert `DO NOTHING`, channel latest advances by `MAX`, membership is
`INSERT OR IGNORE`, and a replayed sequence returns a snapshot rather
than reapplying). `snapshotRequired` takes the same recovery path
instead of being treated as success. If reopen and retry both fail,
native is explicitly degraded and equivalent state is applied to the JS
fallback so `isNative()` stops reporting healthy. Regressions assert the
*originally rejected* marker, destructive clear, and membership delta
each survive; removing the retry takes 5 tests red including all three.

**Blocker 2 — a superseded archive start could tear down the newer
scope's relay session.** `begin` now returns an `ArchiveOwnership` token
that holds both ownership guards, and `archive_session` requires one, so
session acquisition happens inside the ownership critical section rather
than after it.

### Deviation from the requested regression test for blocker 2

Carl asked for "a production-wiring regression that pauses B after
`begin`, fully installs C, resumes B, and proves only C's session and
subscriptions remain live." **We did not write that test, deliberately,
and wrote a stronger one instead.**

Under this fix that scenario is unreachable by construction. The fix is
not a revalidation check that a test could observe passing or failing —
it is a type-level fence: `begin` hands the winner a token that holds
the ownership locks, and `archive_session` cannot be called without one.
A test that staged "B pauses after `begin`, C fully installs" would be
staging a state the type system now forbids, so it could only pass
vacuously.

Two things close the blocker, and only one of them is a test:

1. *A superseded start cannot reach `archive_session` at all.* Enforced
by the compiler, not by a test. `ArchiveOwnership` is un-constructible
outside `archive::sync`. Verified by attempting both bypasses: calling
`archive_session` without a token fails **E0061**, and forging the token
from a real non-archive caller fails **E0451** ("fields `_latest` and
`_running` are private"), with a second independent barrier because
`RunningSync` is itself a private type.
2. *While an owner holds the token, no newer start can claim.* This is
the property the token's usefulness rests on, and it is what the new
regression pins —
`a_newer_start_cannot_claim_while_the_owner_holds_its_token`.

That test is aimed at the one mutant that actually threatens this
design: keeping the token but releasing the guards inside `begin`, which
is what someone reaches for to avoid holding a lock across an await. It
**compiles**, it keeps every other lifecycle test green, and it restores
the exact race. Against it the new test goes **single-red** — the other
26 tests in the module stay green.

Proving mutual exclusion over the whole claim → acquire → install unit
is strictly stronger than staging a pause the type system forbids, which
is why we chose it.

Also recorded in the code, in `ArchiveOwnership`'s doc comment: what
this token does **not** cover. It serializes archive lifecycle against
archive lifecycle, not against a plain `NativeRelayClient::session()`
call from a non-archive feature. That window predates this PR and is
theoretical today given renderer gating, so it is documented rather than
fixed here.

### Round-2 receipts at `9128b9389`

- `cargo test --lib`: **2513 passed / 0 failed**
- `pnpm test`: **4926 / 4926**
- `cargo clippy --all-targets -D warnings`, `cargo fmt --check`, `pnpm
check`: rc 0
- Merged against `origin/main` in a scratch worktree (clean, zero
conflicts) and re-gated on that merged tree: **2544** Rust / **4956**
frontend, both 0 failed


---

## Round 3 — final head `9a8128ba4`

`9a8128ba4d2d695113505162be388fcec62cda0a` is a merge of `origin/main`
(`a282e0643`) into the pack branch, taken **before** final verification
so that verification would run on the bytes CI builds rather than on a
head that was about to move.

The merge is textually clean but touches two files the pack also
touches, `desktop/src-tauri/src/lib.rs` and
`desktop/src/testing/e2eBridge.ts`, so recombination was checked
explicitly rather than inferred from the absence of conflicts: **all 342
pack-added lines in those files survive (0 missing), and all 17
main-added lines survive (0 missing)** — no silent revert in either
direction. Main's contribution is a `#[doc(hidden)] pub use
print_agent_access_owner_only_probe_if_requested` in `lib.rs`, plus a
Bumble→Pollen rename and workflow revision / `workflowUpdateError` mocks
in `e2eBridge.ts`.

### Receipts at `9a8128ba4`

Implementer, in a shell with `git rev-parse HEAD` confirmed at this SHA:

- `cargo test --lib` (full package): **2544 passed / 0 failed** / 18
ignored
- `pnpm test`: **4956 / 4956** across 74 suites
- `cargo clippy --all-targets -D warnings`, `cargo fmt --check`, `pnpm
check`: rc 0
- The 4 remaining `pnpm check` findings (`channelMutesStorage.test.mjs`,
`channelStarsStorage.test.mjs`, `terminal.css`,
`empty-edit-delete.spec.ts`) are all in files this PR never touches
- All 7 pre-push hooks green, including `branch-skew`, which now passes
on its own because the branch contains `origin/main`

Independent reviewer, same exact head, own instruments:

- `cargo test --workspace`: green; `cargo clippy --workspace
--all-targets -- -D warnings`: green *(a later workspace run at
`9a8128ba4` hit pre-existing `main`-side failures in untouched crates —
see the workspace-gate entry under Open gates above; the difference is
flake timing plus which run happened to reach `git-sign-nostr`, not a
change in this PR)*
- `pnpm test`: **4956 / 4956**; `pnpm check`: rc 0, same known findings
outside touched files
- **Mutation proof of the blocker-2 fix:** dropping the ownership
token's hold (`drop(owner)`) turns
`a_newer_start_cannot_claim_while_the_owner_holds_its_token` red;
restoring the shipped bytes returns the suite green. The invariant that
a superseded start cannot claim or disturb a newer archive session is
pinned by a test, not just by the type system.
- **Live-local at this exact head: attested and verified.** The
mis-cited `f079d0914` run that originally appeared here is retracted; a
real run at `9a8128ba4` replaced it, and its receipts were opened
file-by-file (not relayed) by a second agent. See the live-local entry
under Open gates above for the receipt path, wall-clock bounds,
store-count chain, and socket evidence.
- Verdict: minimalness **9/10**, elegance **9/10**, correctness
**9.5/10**. **Score restored** now that the exact-head live-local run
exists and its provenance has been checked from the files (SHA-named
receipt dir, self-dating manifest, receipt mtimes after the commit time,
sealed digests spot-checked). Gatekeeper re-bless at `9a8128ba4`:
**9/10, merge recommended.**

The paired CPU benchmark remains the one gate not run. It is a
measurement of the pack's *benefit*, not of its correctness, and it is
stated as unrun rather than estimated.

---------

Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
…lock#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 block#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 block#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>
…lock#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 block#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>
Serialize the mobile pre-push analysis and test commands into one lane,
mirroring the existing `desktop-tauri-checks` pattern.

The shared Flutter state under `mobile/` (`.dart_tool`, shader bundle,
and `pubspec.lock`) must not be driven by parallel hook processes. The
existing `mobile/**` path filter is unchanged; `mobile-check` now runs
before `mobile-test` in the same lane.

- Replace the parallel `mobile-check` and `mobile-test` commands with
`mobile-checks`.
- Keep the commands serial with `just mobile-check && just mobile-test`.

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

- normalize emoji-mart's poisoned literal `{}` recents state before
initialization
- preserve the established idle emoji-index prewarm so first picker open
stays responsive
- keep the mounted quick-reaction tray behavior unchanged from main
- cover full-picker same-session recents, prewarm ordering/callback
behavior, and the production picker-to-prewarm import boundary

## Validation

- focused prewarm tests passed 2/2 at
`98188797e219db20670b46379a240493ff8f4cbe`
- mutation check passed: changing `EmojiPicker` back to direct
`@emoji-mart/data` consumption fails the new production-boundary
assertion
- pre-push branch-skew, file-size, Desktop check, typecheck, and full
Desktop unit suite passed (5,024/5,024) at exact pushed head
`98188797e219db20670b46379a240493ff8f4cbe`
- focused full-picker lifecycle plus existing custom-emoji/tray smoke
passed 2/2 on the preceding runtime-equivalent head
- independent Royal Court scope review and headless field test passed
the full-picker journey on the preceding runtime-equivalent head

---------

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

- Replace the Link previews and Thread layout dropdowns with the shared
segmented control from block#5644.
- Pair each setting with a real, inert preview: production Compact/Rich
link cards and a simplified Focus/Split thread diagram.
- Keep scrub previews local to Settings so only a completed selection
updates the saved preference and real app behavior.
- Keep each setting and preview responsive as one grouped unit, with
focused light/dark and narrow/wide coverage.

## Dependency

- Stacked on block#5644, which provides the typography/density foundation,
shared `SegmentedControl`, and responsive `SettingsOptionRow`.
- After block#5644 merges, this PR should be retargeted/rebased onto `main`.

## Validation

- `pnpm typecheck`
- `pnpm check:px-text`
- `pnpm check:file-sizes`
- `pnpm exec playwright test tests/e2e/appearance-previews.spec.ts
--project=smoke` (3 passed)
- Focused existing Appearance coverage (2 passed)
- Pre-push desktop checks, typecheck, tests, and Tauri checks

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
## Summary

Large repositories were silently limited to the first 250
lexicographically sorted paths, which could leave the Files tab showing
only dot-directories. Retain all tracked paths while limiting eager
content previews, and replace the Files tab hard cutoff with incremental
200-entry pagination.

### Related issue

Fixes block#4960. No duplicate PR found.

### Testing

- Targeted Rust regression: `cargo test --manifest-path
desktop/src-tauri/Cargo.toml
parse_ls_tree_keeps_paths_after_eager_preview_limit --lib`
- Pagination helper regression: `node --import ./test-loader.mjs
--experimental-strip-types --test
src/features/projects/lib/projectsViewHelpers.test.mjs`
- Desktop Biome, type checking, and file-size checks pass
- All 4,393 desktop tests pass
- Repository pre-push hooks pass
- `just ci` currently stops on pre-existing Clippy warnings in
`crates/buzz-acp/src/queue.rs` and `crates/buzz-agent/src/hints.rs`,
outside this PR diff

Generated with Codex

---------

Signed-off-by: Evan Chen <evanc@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
## Summary

- introduce a `PlaybackCoordinator` as the exclusive owner of the rodio
`Player`
- serialize append, cancellation, synthesis-flight, and drain-release
state
- cancel without blocking the coordinator by replacing the player queue
- preserve first-append/onset bookkeeping across cancellation
- increase the existing playback lead-in from 20 ms (480 samples) to a
derived 22 ms (528 samples), covering rodio 0.22.2's 512-source-sample
add-time bootstrap span

This is the behavior-preserving coordinator prerequisite for the
follow-up floor-authorization policy. The only intentional playout
difference is +2 ms silence at playback-chunk starts to prevent the
measured fresh-player bootstrap chirp.

## Verification (all at this exact head,
`8bfa4c0531834dce39f5ad0266f2674505e659bf`)

- `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --workspace
--all-targets -- -D warnings` — clean
- `cargo test --manifest-path desktop/src-tauri/Cargo.toml --package
buzz-desktop` — 2,596 passed / 17 ignored / 0 failed
- coordinator concurrency tests: stalled cancellation, append-vs-cancel
serialization, cancel election, first-append rearming, and the post-swap
`starts_playback_chunk` invariant
- headless known-answer sweep: 528-sample lead-in clean across 7/7
device configurations (incl. falsifier arm at 192k/96k/32k/16k/8k and
1/2/4/6 ch); current 480 dirty across 5/7
- live CoreAudio gate at production buffer shape (24 kHz mono, real
`build_sentence_append_buffer` path): bootstrap chirp eliminated (660.1
Hz head vs 660 Hz control), no false drain, cancel-to-silence ~1 ms vs
12 ms baseline
- independent line review + mutation testing on the shipping coordinator
bytes: 4/6 mutants killed, 2 shown equivalent under rodio 0.22.2
`Player::drop` semantics

## Playout cost

- +2 ms lead-in at each playback-chunk start versus main
- first post-cancel utterance receives 22 ms total intentional silence
(not stacked cushions)

## Provenance

Authored and reviewed by Buzz agents (Wren: implementation; Dawn: line
review, known-answer control, mutation testing; Max: live CoreAudio
gates; Eva: coordination and final verification), with review records on
the Buzz relay. This PR was briefly merged to main directly by mistake
and reverted via ref reset; this is the same commit going through the
front door.

---------

Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
…block#6335)

## Summary

Part 1 of 4 stacked PRs continuing the Projects work from block#6003.

- The Projects overview now follows the selected section: the right-hand
context pod stays visible across the Projects / Repositories / Reviews /
Tasks / Channels tabs and shows section-specific people, stats, and
contribution graphs, so it reads as live context rather than a detached
summary.
- Entity list rows are consolidated onto one compact line (title first,
icons after, aligned dates/counts), shared across projects,
repositories, PRs, issues, and channel lists via `ProjectEntityListRow`.
- New `projectRelatedChannels` helper resolves the channels a project is
discussed in for the overview.

Follow-ups in this stack: part 2 (selectable workspaces), part 3
(context-aware collaboration), part 4 (navigation & detail-page polish).

## Test plan

- [x] Desktop unit tests (`pnpm test`) — pass
- [x] TypeScript (`tsc --noEmit`), Biome, clippy — clean via pre-push
gate
- [x] Playwright specs updated for the new overview behavior
(`projects-v3-screenshots.spec.ts`, `sidebar.spec.ts`) — pass locally in
the smoke project

---------

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

# Conflicts:
#	desktop/src-tauri/src/lib.rs
#	desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx
Upstream block#6341 moved the global-shortcut plugin construction out of
`global_shortcut_plugin` and into `ptt_shortcut::install`, deleting the old
function. The merge kept both — the two edits landed in different regions of
the file, so git had no conflict to report — and `lib.rs` now calls only
`install`, leaving the old builder unreferenced.

`cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets`
caught it as `function \`global_shortcut_plugin\` is never used`; the file
carries no fork-local content, so this restores it byte-identical to
upstream/main.

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

The Markets feature (#31) has been fork-local for eleven syncs with no row in
the patch table. This sync paid for that: upstream block#6003 restructured
AppSidebarPinnedHeader.tsx around the fork's combined Inbox+Markets row and the
conflict had to be resolved by reconstructing intent from git log. Adds three
rows covering the nav surface, the prop threading, and the additive
implementation paths, plus a section on what actually conflicts and why the
combined row is height-stable.

Also records that #31's PTT relocation is gone. It moved the global-shortcut
plugin out of lib.rs into ptt_shortcut.rs to shed 92 lines under the file-size
ratchet; upstream block#6024 made the same relocation into the same file as
`install()`. Both landed in different regions of the file, so git kept both
copies with no conflict marker, and the fork's became unreferenced — surfacing
only as a tauri-clippy dead-code error that reads like an upstream defect.

The relay.rs row gains this sync's ratchet data point: 996 -> 1003, condensed
back to 999, which spends the last of the comment slack. Names the next move
(returning `pub mod allowlist;` to lib.rs, now 936 lines) so the following
overflow does not get resolved by splitting upstream's code.

Signed-off-by: adrienlacombe <6303520+adrienlacombe@users.noreply.github.com>
@adrienlacombe adrienlacombe added upstream-sync needs-human Sync stopped on a tripwire; a human must review and merge labels Aug 20, 2026
@adrienlacombe
adrienlacombe merged commit 6ff5bd0 into main Aug 20, 2026
35 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.