Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bf139e8
perf(presence): reduce heartbeat frequency (#3783)
tlongwell-block Jul 30, 2026
4d47aa8
feat(desktop): improve agent activity header ui (#3321)
tellaho Jul 30, 2026
1b3ff96
Add Devin as a preset ACP harness (#3225)
fenner888 Jul 30, 2026
dba97ee
fix(db): isolate usage metrics advisory-lock test on scratch DB (#3670)
NanoRisk6 Jul 30, 2026
d40a332
feat(desktop): raise the install ceiling and make installs observable…
wpfleger96 Jul 30, 2026
b9e4ed6
test(desktop): click visible thread collapse guide (#3800)
wesbillman Jul 30, 2026
114d40d
feat(relay): gate kind 30178 team-catalog reads behind the shared tag…
wpfleger96 Jul 30, 2026
29dfe48
fix(desktop): don't gate hover affordances on the hover media query (…
sumit-m Jul 30, 2026
74cd571
fix(desktop): report authenticated relay recovery (#3812)
wesbillman Jul 30, 2026
36571f4
fix(desktop): allow linux-only media items as dead code off-linux (#3…
wpfleger96 Jul 30, 2026
23f0c26
fix(relay): align NIP-11 max_limit with REQ ceiling (#3635)
wpfleger96 Jul 30, 2026
ede2686
fix(desktop): align data deletion labels (#2230)
baxen Jul 30, 2026
9e8fcfd
fix(desktop): channel topic and membership metadata cleanup (#3642)
delkc Jul 30, 2026
f3e5e81
fix(catalog): update Amp tagline (#3806)
AJKemps Jul 30, 2026
468647a
feat(desktop): locally stored NIP-49 encrypted key backup (#2937)
tlongwell-block Jul 31, 2026
10d5a26
feat(relay): raise hosted community limit to five (#3829)
kalvinnchau Jul 31, 2026
d88313f
feat(desktop): delete a message by clearing its edit to empty (#3813)
sw-square Jul 31, 2026
d48b0e0
feat(desktop): upgrade Pocket TTS model (#3266)
johnmatthewtennant Jul 31, 2026
081f805
feat(agent): optional reply guard reminds a silent turn to publish (#…
tlongwell-block Jul 31, 2026
4632c55
feat(desktop): auto-enable huddle transcription for agents (#3180)
johnmatthewtennant Jul 31, 2026
689617a
docs: add VISION_REMOTE_AGENTS.md (#3924)
tlongwell-block Jul 31, 2026
61ba9df
refactor(voice): extract reusable Pocket primitives + Pocket voice se…
tlongwell-block Jul 31, 2026
39ce3df
fix(desktop): open profiles from avatars (#3751)
klopez4212 Jul 31, 2026
18ba3a6
Merge upstream block/buzz into adrienlacombe/buzz (23 commits)
adrienlacombe Jul 31, 2026
0d9a5c3
docs(agents): record the 30178 -> 30900 kind move and drop the linux_…
adrienlacombe Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ jobs:
./scripts/start-relay-for-tests.sh --no-build
- name: Relay E2E tests
run: |
cargo test -p buzz-test-client --test e2e_persona --test e2e_nostr_interop -- --ignored --nocapture
cargo test -p buzz-test-client --test e2e_persona --test e2e_team_catalog --test e2e_nostr_interop -- --ignored --nocapture
cargo test -p buzz-test-client --test e2e_relay invite -- --ignored --nocapture
cargo test -p buzz-test-client --test e2e_relay nip43_membership_snapshots_are_rejected -- --ignored --nocapture
env:
Expand Down
62 changes: 52 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ When a file edit is genuinely unavoidable:
- If it is a genuine upstream bug, consider sending it to `block/buzz` instead of
carrying the patch. A merged upstream fix removes divergence permanently.

**When upstream lands a fix that makes a fork patch redundant, delete the fork
patch** — and delete its row from the table below in the same commit, so the table
never describes a patch that is no longer there. Carrying both is permanent
conflict surface for nothing.

That happened to `desktop/src-tauri/src/linux_media.rs` in the 2026-07-31 sync.
The fork carried a module-level `cfg_attr(not(linux), allow(dead_code))` because
`PROD_ORIGIN`, `DEV_ORIGIN` and `is_trusted_media_origin` are reachable only from
the `cfg(linux)` `enable_media_capture` and the tests, so `clippy -- -D warnings`
failed on the lib target on macOS and broke the pre-push hook for Mac developers.
Upstream's `36571f4ad` (#3811) added per-item allows covering exactly those three
items, so the fork patch was dropped. **Verify before deleting** — the check that
settled it was running `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml
--all-targets -- -D warnings` on macOS with the patch removed, not reading the
upstream diff and assuming.

### How the sync works

The sync runs in **two stages**, and which one produced a PR determines what you
Expand Down Expand Up @@ -144,8 +160,10 @@ place.
| `.github/workflows/upstream-sync.md` + `.lock.yml` | new | The agentic (02:00) sync stage. Edit the `.md` and run `gh aw compile upstream-sync`; the body is **not** inlined into the `.lock.yml`, which stores only a `body_hash`, so a body-only edit shows up as a one-line lock diff |
| `.github/workflows/upstream-sync-merge.yml` | new | The deterministic (01:30) sync stage — the one that preserves the merge parent. Plain git, no AI. Optional `SYNC_PUSH_TOKEN` secret: a branch pushed with `GITHUB_TOKEN` does not start new workflow runs, so set a PAT if CI stops firing on sync PRs |
| `.github/workflows/upstream-sync-ci-status.yml` | new | Labels an open sync PR `sync-ci-green`/`sync-ci-red` once checks settle, and re-requests the Copilot review that gh-aw's `reviewers:` fails to attach. Deliberately does not merge |
| `desktop/src-tauri/src/linux_media.rs` | module-level `cfg_attr(not(linux), allow(dead_code))` | Three items are reachable only from the `cfg(linux)` `enable_media_capture` and the tests, so `clippy -- -D warnings` fails on the lib target on macOS/Windows, breaking the pre-push hook for Mac developers. Upstream lints on Linux only and never sees it. An `allow` rather than a `cfg` because `mod tests` unit-tests them on every platform by design — gating to Linux would trade three lint errors for three broken tests. Belongs upstream |
| `crates/buzz-relay/src/handlers/ingest.rs`, `crates/buzz-sdk/src/builders.rs` | NIP-SW Starknet wallet binding (`KIND_STARKNET_WALLET_BINDING`, kind:30178) | Fork-only feature woven into upstream files: the ingest-time on-chain attestation check (which cannot move to `handle_side_effects`, since those run after storage — too late to reject) and the SDK builder. Merged cleanly through the 23-commit sync, but they are real divergence in files upstream edits often |
| `crates/buzz-relay/src/handlers/ingest.rs`, `crates/buzz-sdk/src/builders.rs` | NIP-SW Starknet wallet binding (`KIND_STARKNET_WALLET_BINDING`, **kind:30900** — was 30178, see [Fork-local event kinds](#fork-local-event-kinds)) | Fork-only feature woven into upstream files: the ingest-time on-chain attestation check (which cannot move to `handle_side_effects`, since those run after storage — too late to reject) and the SDK builder. Real divergence in files upstream edits often — `ingest.rs` conflicted three ways in the 2026-07-31 sync, all keep-both |
| `crates/buzz-core/src/kind.rs` | `KIND_STARKNET_WALLET_BINDING` + its `is_parameterized_replaceable` assertion, in a fork-local block after the NIP-34 git kinds | Deliberately *not* beside the upstream `30174`–`30178` cluster. See [Fork-local event kinds](#fork-local-event-kinds) |
| `migrations/0027_wallet_binding_fts.sql`, `0028_wallet_binding_fts_kind_move.sql` | new | NIP-SW search exclusion (`0027`) and its re-point after the 30178 → 30900 move (`0028`). Additive files, so they never conflict — but `0028` exists only because `0027`'s kind literal could not be edited (sqlx checksums). **Never edit an applied migration**; add a follow-on |
| `crates/buzz-db/src/migration.rs` | `migrations.len()` assertion is 28, not upstream's 26 | Counts embedded migrations, so it moves whenever the fork adds one. `0027` landed without bumping it and left the test failing on `main`; fixed in PR #9. A one-integer conflict on every upstream migration — take upstream's count and add the fork's two |
| `.github/workflows/macos-canary.yml` | new; `push` trigger on `main` with desktop path filters | Unsigned macOS canary; upstream only has a *signed* one, which a fork cannot run. Builds automatically when `desktop/**`, `crates/**` or the root `Cargo.*` change, so the newest artifact always matches `main` — it was dispatch-only, and the sole artifact went 13 commits stale. Free: the repo is public, so GitHub-hosted macOS runners are unbilled. Stages the artifact and the usage notes under the product name read from `tauri.conf.json`, not a hardcoded one, so the brand rename below cannot publish a build under the old name |
| `.github/aw/actions-lock.json` | new | gh-aw action SHA pins |
| `.gitattributes` | `*.lock.yml linguist-generated` | Added by `gh aw init` |
Expand Down Expand Up @@ -201,29 +219,53 @@ belongs in that block. Upstream's parameterized-replaceable kinds cluster at
`30174`–`30178` and grow upward, so anything the fork places near them will be
claimed sooner or later.

That is not hypothetical. The fork put NIP-SW's Starknet wallet binding at
`30178`; upstream then shipped `KIND_TEAM_CATALOG = 30178` (#3358), and the
resulting collision was two unrelated schemas on one integer in the same crate —
`ingest_event_inner` would run both the on-chain attestation verifier and
`validate_team_catalog_envelope` on every such event, so one always rejects the
other's traffic. Text merging cannot fix that; the number has to move.
That is not hypothetical, and it has already happened once. The fork put NIP-SW's
Starknet wallet binding at `30178`; upstream then shipped `KIND_TEAM_CATALOG =
30178` (#3358), and the resulting collision was two unrelated schemas on one
integer in the same crate — `ingest_event_inner` would run both the on-chain
attestation verifier and `validate_team_catalog_envelope` on every such event, so
one always rejects the other's traffic. Text merging cannot fix that; the number
has to move.

**The rule when upstream claims a kind the fork already uses:** upstream keeps the
integer, the fork's constant moves into the reserved block. Keep both constants and
both behaviours — never resolve a kind collision by picking a side. `30178` stays
upstream's; the wallet binding is `30900`.
both behaviours — never resolve a kind collision by picking a side.

**That move landed in the 2026-07-31 sync (PR #9).** `30178` is upstream's
team catalog; `KIND_STARKNET_WALLET_BINDING` is **`30900`**. The constant also
moved *position* in `kind.rs` — out of upstream's `30174`–`30178` cluster and into
a marked fork-local block after the NIP-34 git kinds, because that cluster is
exactly where upstream adds new parameterized-replaceable kinds. Leaving a fork
constant inside it re-creates this conflict on every such addition. **Put new
fork-local kinds in that block, not next to the upstream kind they relate to.**

Moving a kind is a **wire-format change**: events already stored under the old
integer are not rewritten, and clients pinned to it stop matching. Check for
existing events before moving one that has been live.

It is also a **search-exclusion change**, which is the part that is easy to miss.
The NIP-SW full-text exclusion is a `kind = …` literal baked into a `search_tsv`
generated column by migration `0027`. Applied migrations never re-run and
`sqlx::migrate!` validates their checksums, so the old file cannot be edited —
editing it fails relay startup with a version mismatch. The renumber therefore
needed a *follow-on* migration (`0028`) that peels `0027`'s wrapper and re-wraps on
the new integer. Without it both halves break at once: addresses at the new kind
become searchable, and upstream's events at the old integer get excluded instead.
**Any future kind move carrying an FTS exclusion needs the same follow-on.**

When adding a fork-local kind, the checklist is the constant in
`buzz-core/src/kind.rs`, its `is_parameterized_replaceable` assertion,
`SHARED_GATED_KINDS` if it is shareable, the relay's `required_scope_for_kind` and
ingest branch, the SDK builder in `buzz-sdk/src/builders.rs`, any `buzz-cli`
subcommand, and `desktop/src/shared/constants/kinds.ts` plus
`mobile/lib/shared/relay/nostr_models.dart`, which must stay in sync.

Two items on that checklist were missed when the wallet binding first landed, so
check them explicitly: the `is_parameterized_replaceable` assertion (added in PR
#9), and — if the kind gets its own migration — the `migrations.len()` assertion in
`buzz-db/src/migration.rs`, which `0027` left stale at upstream's count and which
therefore failed on `main` until PR #9. Adding any migration means bumping it.

### Repo settings (no file changes — preferred mechanism)

| Setting | Value | Why |
Expand Down
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ The subscriber uses a **dedicated** `redis::aio::PubSub` connection — not from

**Reconnection:** exponential backoff 1s → 30s (`backoff_secs * 2`). Backoff resets to 1s only after a clean stream end, not on each reconnect attempt.

**Presence:** `SET buzz:presence:{pubkey_hex} {status} EX 90` — 90-second TTL (3× the 30-second heartbeat interval). Single missed heartbeat does not cause presence flap.
**Presence:** `SET buzz:presence:{pubkey_hex} {status} EX 180` — 180-second TTL (3× the 60-second heartbeat interval). Single missed heartbeat does not cause presence flap.

**Typing indicators:**
```
Expand Down Expand Up @@ -797,7 +797,7 @@ Docker Compose provides the full local development stack. All services include h
| Pattern | Type | TTL | Purpose |
|---------|------|-----|---------|
| `buzz:channel:{uuid}` | Pub/Sub channel | — | Event fan-out (single-community form; shared multi-community Redis must use `buzz:{community}:channel:{uuid}` or equivalent) |
| `buzz:presence:{pubkey_hex}` | String | 90s | Online/away status (single-community form; shared multi-community Redis must scope by community) |
| `buzz:presence:{pubkey_hex}` | String | 180s | Online/away status (single-community form; shared multi-community Redis must scope by community) |
| `buzz:typing:{channel_uuid}` | Sorted Set | 60s | Active typers (5s window; shared multi-community Redis must scope by community) |

### Full-Text Search (Postgres FTS)
Expand Down
Loading
Loading