mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
## Why PR #6660 introduced four PostgreSQL-backed persistence tests and a focused CI selector, but an exact test-name list does not automatically cover future database tests. Broad ignored-test execution also exposed shared-schema races and ambiguity between desired-state and migration-applied schema expectations. ## What - Establish a discoverable convention: PostgreSQL unit modules use `postgres_tests`, PostgreSQL integration binaries use a `postgres_` prefix, and unrelated external-infrastructure tests use an `external_infra_` prefix. - Add a dedicated nextest PostgreSQL profile and archive-backed CI job covering the relevant crates without enumerating test names. - Create a run-scoped desired-state source database and a unique PostgreSQL database per test process for parallel-safe isolation, with cleanup on success, failure, or interruption. - Route destructive migration and migration-parity tests to clean `template0` databases while desired-state tests clone the desired-state database. - Document discovery, schema modes, required role privileges, and a bounded portable local runner. - Keep the existing infrastructure-free unit-test jobs unchanged. This is intentionally limited to test and CI harness behavior. It does not move production database code, change persistence semantics, or implement issue #20's broader shared test-utility refactor. ## Risk Low production risk because all changes are confined to tests, CI configuration, documentation, and test harness scripts. Remaining operational risks: - Cleanup retries `dropdb --force` five times across roughly four seconds. Exhaustion warns with the database name but deliberately does not mask the test result; individual PostgreSQL diagnostics are suppressed. - The seven-package boundary is duplicated between the runner and CI archive and must remain synchronized if PostgreSQL tests move to a new crate. - Portable SHA fallback branches were exercised on Linux; no macOS Blox workstation was needed for this Linux CI artifact. - GitHub's unchanged generic Unit Tests job currently fails while cold-linking `buzz-voice` because `sherpa-onnx-c-api` is absent. The branch changes no voice/build/toolchain inputs; the exact parent previously passed that job, and Blox passes the same infrastructure-free suite (1,450/1,450). A prior-head one-job retry and the final-head run both reproduced the hosted-runner failure. ## Verification Author Blox workstation `2020088`: - `cargo fmt --all -- --check` - `just clippy` - `just test-unit`: 1,450 passed; PostgreSQL tests remain skipped in fast jobs - PR #6660 focused tests: 4/4 passed through the final runner - Full lane: 282/282 passed in 10.087s with a non-superuser role limited to `CREATEDB`, `CREATEROLE`, and `pg_read_all_stats` - 272 desired-state tests - 10 migration-applied tests - 6 explicitly filtered external-infrastructure tests - Cleanup fault injection: a deliberately underprivileged 282-test run produced the expected 277 passes and 5 failures, and the post-run catalog audit found zero lane databases after retry cleanup. The restored successful run also left zero lane databases. GitHub exact final head `92c231e48f299ea2af23817763f5ae80ac013d68`: - [PostgreSQL Tests](https://github.com/block/buzz/actions/runs/32789030203/job/97630827823): 282/282 passed across 10 binaries in 98.886s; 1,240 skipped, including 6 via the profile filter - The shared relay/PostgreSQL archive predecessor completed successfully - [Unit Tests](https://github.com/block/buzz/actions/runs/32789030203/job/97626930420): unrelated `sherpa-onnx-c-api` native-link failure described above - Independent exact-head review on separate Blox workstation `2022762`: no substantive findings after all initial findings were addressed. The reviewer independently verified cleanup retry behavior, exit-status preservation, shell portability, syntax, and a clean exact-head worktree. ## References - Stacked on #6660 at exact head `561de54be4d9c2b622b7c2aa5b61bc3068f47e2a` - https://github.com/TheSentinel454/buzz/issues/20 - https://github.com/TheSentinel454/buzz/issues/20#issuecomment-5398526792 Generated with Codex. ## Update — August 24, 2026 review follow-up - Centralized the repeated PostgreSQL test URL resolution in crate-local, test-only helpers without introducing the broader shared utility refactor from issue #20. - Restored descriptive hybrid/Redis test function names. Structural `external_infra_*_tests` modules now own exclusion, and the nextest filter only recognizes module path segments. - Added a three-second source guard that scans every Rust file and fails CI when an ignored PostgreSQL test would be omitted or an external-infrastructure test would be included. Fixture tests cover accepted modules/binaries and both failure modes. - Removed the two implementation-plan documents. Performance profiling on Blox workstation `2027352` (same prebuilt archive and cargo-nextest 0.9.143 for every comparison): - Current per-test database model, 8 workers: 10.15–10.40s across three runs. The 283 `createdb` calls and 284 `dropdb` calls consumed 28.85–29.46 aggregate seconds, about 39–40% of aggregate test-process duration. - Reusing one database per worker without cleanup: 6.55–6.84s, about 35% faster, but all three runs failed because global matcher-queue tests inherited a quiesced community from an earlier test; the failing test varied with scheduling. - Reusing workers with `TRUNCATE … RESTART IDENTITY CASCADE`: all 282 tests passed, but total time regressed to 10.47–10.67s. The 265 truncate calls consumed 24.34–25.45 aggregate seconds, so truncation merely replaced most clone/drop cost. - Raising concurrency to 16 workers reduced one isolated-database run to 8.79s, but 24 workers exposed a cluster-global `pg_stat_activity` race. The profile remains at the proven-safe 8 workers. Decision: retain unique per-test databases. Harness cleanup uses `dropdb --if-exists --force`; it drops the database rather than truncating tables or deleting rows. Verification at local branch head `60086c1f6ee34a193c342255a5e8e6293b60e988`: - Discovery inventory: exactly 282 intended tests; all 6 hybrid/Redis tests present and structurally excluded. - PR #6660 focused persistence tests: 4/4 passed. - Full PostgreSQL lane: 282/282 passed three times; 1,240 skipped each run; wall time 9.72–9.96s without profiling shims. - Non-ignored tests with a desired-state database: 1,234/1,234 passed; one mesh-demo timeout from the first run passed immediately in isolation. - `cargo fmt --all -- --check` and all-target/all-feature clippy for `buzz-db`, `buzz-deletion`, and `buzz-relay` passed. Final independent re-review at exact head `42097c0136ab3fa8f0efe5720ae829c04575b9b6` on separate Blox workstation `2028455`: no substantive residual findings. The reviewer independently exercised ordinary strings, zero/one/three-hash raw strings, line/block comments, and string-contained lookalikes, then reran the full 353-file scan, shell syntax, Python AST parsing, and diff check. Independent review found that the source guard's initial regular expression could miss raw-string ignore reasons and treat a commented attribute as real. Final head `42097c0136ab3fa8f0efe5720ae829c04575b9b6` parses valid ordinary/raw Rust string literals only at attributes found in comment-sanitized source; regression fixtures cover both cases. The fixture suite, Python compilation, shell syntax, diff check, and full 353-file repository scan pass on Blox. ## Update — August 25, 2026 restack PR #6660 merged, so this follow-up was rebased from its former exact parent onto current `origin/main` (`8d2d0ff5ad42733e9949442c4b6358d0ba87f9a8`). The final candidate head is `05dcc2ab28948e3ab79bb44839a69f2ba44648a2`; the PR no longer carries #6660's pre-squash history. Fresh exact-head verification on Blox workstation `2028572`: - Discovery guard passed across 353 Rust files; inventory remained exactly 282 intended PostgreSQL tests. - PR #6660 focused persistence tests: 4/4 passed. - Full PostgreSQL lane: 282/282 passed three times; 1,243 skipped each run; wall time 9.70–9.87s. - `cargo fmt --all -- --check` and all-target/all-feature clippy for `buzz-db`, `buzz-deletion`, and `buzz-relay` passed. ## Update — August 25, 2026 final hosted verification - [GitHub PostgreSQL Tests](https://github.com/block/buzz/actions/runs/32801679226/job/97667835080): 282/282 passed in 53.787s; 1,243 skipped. - The final guard scanned all 353 Rust files successfully before the hosted lane ran. - The shared relay/archive prerequisite and infrastructure-free Unit Tests job both passed at exact final head `42097c0136ab3fa8f0efe5720ae829c04575b9b6`. --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> Signed-off-by: tornquist <tornquist@squareup.com>
592 lines
22 KiB
Markdown
592 lines
22 KiB
Markdown
# Contributing to Buzz
|
|
|
|
Welcome, and thank you for your interest in contributing! Buzz is an
|
|
open-source project and we're glad you're here. This guide will help you
|
|
get from zero to a merged pull request.
|
|
|
|
If you have questions that aren't answered here, [open an issue](https://github.com/block/buzz/issues/new).
|
|
|
|
If you believe you found a security vulnerability, do not open a public issue.
|
|
Follow our [security policy](SECURITY.md) to submit a private vulnerability
|
|
report instead.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Code of Conduct](#code-of-conduct)
|
|
2. [Before You Open a PR](#before-you-open-a-pr)
|
|
3. [Setting Up the Development Environment](#setting-up-the-development-environment)
|
|
4. [Running Tests](#running-tests)
|
|
5. [Code Style](#code-style)
|
|
6. [Making a Pull Request](#making-a-pull-request)
|
|
7. [Architecture Overview](#architecture-overview)
|
|
8. [Ecosystem](#ecosystem)
|
|
9. [How to Add a New Event Kind](#how-to-add-a-new-event-kind)
|
|
10. [How to Add a New MCP Tool](#how-to-add-a-new-mcp-tool)
|
|
11. [How to Add a New API Endpoint](#how-to-add-a-new-api-endpoint)
|
|
12. [License and CLA](#license-and-cla)
|
|
|
|
---
|
|
|
|
## Code of Conduct
|
|
|
|
This project follows the [Contributor Covenant v2.1](CODE_OF_CONDUCT.md).
|
|
By participating you agree to uphold these standards. Please report
|
|
unacceptable behavior to **conduct@buzz-relay.org**.
|
|
|
|
---
|
|
|
|
## Before You Open a PR
|
|
|
|
Before starting, search [open PRs](https://github.com/block/buzz/pulls) and [open issues](https://github.com/block/buzz/issues) for duplicates — someone may already be working on the same thing. When you open your PR, link the closest existing one in the description (or say "none found").
|
|
|
|
For anything beyond a small fix, opening an issue first is strongly recommended. Describe the problem and proposed solution so a maintainer can acknowledge the approach before you build — it avoids two people building the same thing in parallel.
|
|
|
|
Buzz is an agent platform, so AI-assisted PRs are welcome. No need to disclose the tools you used, but you own and must have reviewed the final code. Submissions that are clearly unreviewed may be closed with a pointer here.
|
|
|
|
We squash-merge, so your PR title becomes the commit subject in `main`. Use [Conventional Commits](https://www.conventionalcommits.org/) format: `feat(mcp): add get_feed_actions tool`. The type prefix (`feat`, `fix`, `docs`, `refactor`, `test`, `chore`) is required. See the [Commit Messages](#commit-messages) section for the full reference.
|
|
|
|
### Sign Your Commits
|
|
|
|
```bash
|
|
git commit -s
|
|
```
|
|
|
|
Every commit needs a Developer Certificate of Origin (DCO) sign-off. The `-s` flag appends a `Signed-off-by` trailer that certifies you wrote the change and can contribute it under the project license. The **DCO Check** will block your PR without it.
|
|
|
|
#### Fix unsigned commits already pushed
|
|
|
|
```bash
|
|
git rebase --signoff main
|
|
git push --force-with-lease
|
|
```
|
|
|
|
#### Auto-setup for future commits
|
|
|
|
```bash
|
|
just hooks
|
|
```
|
|
|
|
This installs a `commit-msg` hook that adds the sign-off trailer automatically for `git commit` and `git merge`. Other flows (`git rebase`, `git cherry-pick`) still need their own flag — `--signoff` and `-s` respectively.
|
|
|
|
We review as capacity allows — focused PRs that follow this guide move fastest.
|
|
|
|
---
|
|
|
|
## Setting Up the Development Environment
|
|
|
|
### Prerequisites
|
|
|
|
| Tool | Version | Notes |
|
|
|------|---------|-------|
|
|
| Rust | 1.88+ | Install via [rustup](https://rustup.rs/) |
|
|
| Node.js | 24+ | Required for desktop app commands and `just ci` |
|
|
| pnpm | 10+ | Required for desktop app commands and `just ci` |
|
|
| Flutter | 3.41+ | Required for mobile app — install via [flutter.dev](https://docs.flutter.dev/get-started/install) |
|
|
| Docker | 24+ | For Postgres, Redis, MinIO |
|
|
| `just` | latest | Task runner — `cargo install just` |
|
|
| `lefthook` | 2.1.3 (Hermit-pinned) | Auto-installed by `just hooks` — no manual install needed |
|
|
| `sqlx` migrations | workspace crate | `just migrate` applies embedded migrations from `migrations/` |
|
|
|
|
This repo uses [Hermit](https://cashapp.github.io/hermit/) for toolchain
|
|
pinning. Activate it once per shell session:
|
|
|
|
```bash
|
|
. ./bin/activate-hermit
|
|
```
|
|
|
|
Hermit pins Rust, `just`, Node, pnpm, and other tools to the versions in
|
|
`bin/`. Each tool is downloaded on first use. You can also run `just bootstrap`
|
|
(which `just setup` calls automatically) to pre-download all required tools
|
|
upfront. If you don't use Hermit, ensure your toolchain meets the minimum
|
|
versions in the table above.
|
|
|
|
#### Linux: Tauri system libraries
|
|
|
|
Hermit pins language toolchains, not system libraries. On Linux, the desktop
|
|
app's Rust crates link against GTK and WebKitGTK, so `just ci` (and any
|
|
`just desktop-tauri-*` recipe) needs these installed system-wide first. On
|
|
Debian/Ubuntu:
|
|
|
|
```bash
|
|
sudo apt-get install -y --no-install-recommends \
|
|
build-essential curl file libasound2-dev libayatana-appindicator3-dev \
|
|
libgtk-3-dev librsvg2-dev libssl-dev libwebkit2gtk-4.1-dev libxdo-dev \
|
|
patchelf wget
|
|
```
|
|
|
|
This is the same list CI installs (see `.github/workflows/ci.yml`), so matching
|
|
it locally keeps your results comparable to CI. Other distributions ship these
|
|
under different package names — see the
|
|
[Tauri prerequisites](https://tauri.app/start/prerequisites/) for the
|
|
equivalents.
|
|
|
|
Without them, `just ci` fails partway through `just check` with a pkg-config
|
|
error such as:
|
|
|
|
```
|
|
The system library `gdk-pixbuf-2.0` required by crate `gdk-pixbuf-sys` was not found.
|
|
```
|
|
|
|
If you're only touching the relay, CLI, or other server-side crates, you can
|
|
skip this and run the narrower recipes instead — `just fmt-check`, `just
|
|
clippy`, `just test-unit`, and `just test` need no GTK.
|
|
|
|
### First-Time Setup
|
|
|
|
```bash
|
|
# 1. Clone the repo
|
|
git clone https://github.com/block/buzz.git
|
|
cd buzz
|
|
|
|
# 2. Activate Hermit (optional but recommended)
|
|
. ./bin/activate-hermit
|
|
|
|
# 3. Bootstrap tools + infrastructure
|
|
just setup
|
|
|
|
# 4. Install Git hooks (optional, recommended)
|
|
just hooks
|
|
```
|
|
|
|
`just setup` runs `just bootstrap` first — it copies `.env.example` to `.env`
|
|
if it doesn't already exist, and invokes `cargo`, `node`, and `pnpm` to trigger
|
|
Hermit's lazy tool download (each tool is fetched once on first invocation and
|
|
cached thereafter). You can also run `just bootstrap` independently at any time;
|
|
it is safe to re-run.
|
|
|
|
`just setup` then starts Docker services (Postgres on `:5432`, Redis on `:6379`,
|
|
Adminer on `:8082`, Keycloak on `:8180` for local OAuth/OIDC testing, MinIO on
|
|
`:9000` for media storage, and Prometheus on `:9090` for metrics) and runs all
|
|
pending database migrations.
|
|
|
|
### Running the Relay and Desktop App
|
|
|
|
```bash
|
|
just dev # starts the relay + desktop app in one command
|
|
```
|
|
|
|
`just dev` builds all agent tools, starts the relay (`ws://localhost:3000`) in
|
|
the background, and launches the Tauri desktop app. The relay process is
|
|
automatically killed when you quit the app or press Ctrl+C.
|
|
|
|
For a split-terminal workflow (relay logs visible separately from Vite output):
|
|
|
|
```bash
|
|
just relay # terminal 1 — relay on ws://localhost:3000
|
|
just desktop-dev # terminal 2 — Vite dev server only (no Tauri shell)
|
|
```
|
|
|
|
### Stopping / Resetting
|
|
|
|
```bash
|
|
just down # Stop Docker services, keep data
|
|
just reset # Wipe all dev state and recreate it; installed Buzz is preserved
|
|
```
|
|
|
|
Development desktop state uses separate bundle identifiers
|
|
(`xyz.block.buzz.app.dev` and per-worktree variants), a separate keyring service
|
|
(`buzz-desktop-dev`), and `~/.buzz-dev`. `just reset` removes those dev-only
|
|
locations and the local Docker volumes. It does not touch the installed app's
|
|
`xyz.block.buzz.app` data, `buzz-desktop` keyring service, or `~/.buzz` nest.
|
|
|
|
---
|
|
|
|
## Running Tests
|
|
|
|
### Unit Tests (no infrastructure required)
|
|
|
|
```bash
|
|
just test-unit
|
|
```
|
|
|
|
Unit tests are self-contained and run without Docker. They cover event
|
|
parsing, filter matching, auth logic, workflow YAML parsing, and more.
|
|
|
|
### Integration Tests (requires running infrastructure)
|
|
|
|
```bash
|
|
just test
|
|
```
|
|
|
|
Integration tests spin up the relay and exercise the full stack — WebSocket
|
|
connections, NIP-42 auth, event ingestion, search indexing, and workflow
|
|
execution. `just test` starts Docker services automatically if they're not
|
|
already running.
|
|
|
|
### PostgreSQL-backed tests
|
|
|
|
PostgreSQL-backed tests run in a dedicated nextest lane. Mark them ignored with
|
|
a PostgreSQL reason and place them in a module whose name ends in
|
|
`postgres_tests`. Standalone integration-test targets use a `postgres_`
|
|
filename prefix instead. Tests that also require infrastructure beyond
|
|
PostgreSQL and Redis live under an `external_infra*_tests` module and are
|
|
excluded without changing their descriptive function names.
|
|
|
|
See the [buzz-db testing guide](crates/buzz-db/TESTING.md) for the crate-level
|
|
checklist.
|
|
|
|
`scripts/test-postgres-test-discovery.sh` enforces the convention across every
|
|
Rust source file. It fails CI when an ignored PostgreSQL test would be omitted,
|
|
or when a Redis-only or hybrid test is accidentally included, so module or file
|
|
renames cannot silently change lane membership. The archive and runner derive
|
|
their Cargo package set from the same markers, so a database test in a new crate
|
|
does not require a separate package-list update.
|
|
|
|
The `postgres-ci` nextest profile creates one database per test process, so
|
|
destructive and concurrent tests must use the database URL supplied through
|
|
`BUZZ_TEST_DATABASE_URL`, `TEST_DATABASE_URL`, or `DATABASE_URL`; do not
|
|
hard-code the shared development database. Ordinary tests receive the committed
|
|
desired-state schema from `schema/schema.sql`. Tests under
|
|
`migration::postgres_tests` receive an empty database and own the embedded
|
|
migration lifecycle. A test outside that module whose behavior intentionally
|
|
depends on migration-created triggers or seed rows uses a
|
|
`migration_schema_` function-name prefix and also receives an empty database
|
|
with `BUZZ_TEST_SCHEMA_MODE=migration`. Test helpers that normally call the
|
|
migrator honor `BUZZ_TEST_SCHEMA_MODE=desired` so the desired-state contract is
|
|
not re-migrated.
|
|
|
|
Tests that inspect cluster-wide PostgreSQL state or open least-privilege
|
|
sessions use a `cluster_global_` function-name segment; migration-backed cases
|
|
use `migration_schema_cluster_global_`. Nextest serializes this small group
|
|
while the database-isolated remainder stays parallel.
|
|
|
|
The setup process requires a PostgreSQL role that can create and drop databases
|
|
and owns the databases it creates; the harness itself does not require
|
|
superuser access. The complete inventory includes privilege-boundary tests that
|
|
create temporary roles and inspect all sessions, so grant that role
|
|
`CREATEROLE` and membership in `pg_read_all_stats` (or use an ephemeral
|
|
superuser, as CI does).
|
|
Set `BUZZ_POSTGRES_ADMIN_URL` to that role's maintenance database, and set
|
|
`PGHOST`, `PGPORT`, `PGUSER`, and `PGPASSWORD` for the desired-state
|
|
schema bootstrap. PostgreSQL client tools are resolved from `PATH` unless
|
|
`PG_BIN_DIR` is set. Tests that use Redis read `REDIS_URL`.
|
|
|
|
With native PostgreSQL and Redis running, the complete lane is below. The
|
|
runner bounds compilation to the packages discovered from the current source
|
|
tree and removes the run-scoped desired-state source database on exit.
|
|
Per-test and source-database cleanup retries transient PostgreSQL disconnect
|
|
races and emits a warning if all five attempts fail.
|
|
|
|
```bash
|
|
./scripts/postgres-test-run.sh
|
|
```
|
|
|
|
### End-to-End Tests
|
|
|
|
End-to-end tests live in `crates/buzz-test-client/tests/`:
|
|
|
|
- `e2e_relay.rs` — WebSocket relay tests
|
|
- `e2e_mcp.rs` — MCP tool tests
|
|
- `e2e_nostr_interop.rs` — Nostr protocol interoperability tests
|
|
- `e2e_media.rs` — media upload/download tests
|
|
- `e2e_media_extended.rs` — extended media tests (GIF, image processing)
|
|
|
|
Run them with (requires running infrastructure):
|
|
|
|
```bash
|
|
cargo test -p buzz-test-client -- --ignored
|
|
```
|
|
|
|
See `TESTING.md` for the full multi-agent E2E testing guide.
|
|
|
|
### CI Gate
|
|
|
|
Before opening a PR, run the full CI gate locally:
|
|
|
|
```bash
|
|
just ci
|
|
# Runs: check + unit tests + desktop build + Tauri check + mobile tests
|
|
```
|
|
|
|
This is the same check that runs in CI. PRs that fail `just ci` will not be
|
|
merged. If `just ci` fails on formatting, `just fix-all` fixes it in one shot (`rustfmt` + Tauri fmt + desktop, web, and mobile formatters).
|
|
|
|
---
|
|
|
|
## Code Style
|
|
|
|
### Formatting
|
|
|
|
We use `rustfmt` with default settings. Format your code before committing:
|
|
|
|
```bash
|
|
cargo fmt --all
|
|
```
|
|
|
|
To check without modifying:
|
|
|
|
```bash
|
|
cargo fmt --all -- --check
|
|
```
|
|
|
|
### Linting
|
|
|
|
We use `clippy` with warnings-as-errors:
|
|
|
|
```bash
|
|
cargo clippy --all-targets --all-features -- -D warnings
|
|
```
|
|
|
|
Fix all clippy warnings before submitting a PR. If you believe a warning is
|
|
a false positive, add a targeted `#[allow(...)]` with a comment explaining
|
|
why.
|
|
|
|
### No Unsafe Code
|
|
|
|
All crates enforce `#![deny(unsafe_code)]`. Do not add unsafe blocks. If you
|
|
believe unsafe is genuinely necessary, open an issue first to discuss the
|
|
approach.
|
|
|
|
### Error Handling
|
|
|
|
- Use `thiserror` for library error types.
|
|
- Use `anyhow` for binary / application-level error propagation.
|
|
- Do not use `unwrap()` or `expect()` in production code paths. Use `?` or
|
|
explicit error handling. `unwrap()` is acceptable in tests.
|
|
|
|
### Logging and Tracing
|
|
|
|
Use the `tracing` crate for all instrumentation. Prefer structured fields
|
|
over string interpolation:
|
|
|
|
```rust
|
|
// Good
|
|
tracing::info!(channel_id = %id, event_kind = kind, "Event ingested");
|
|
|
|
// Avoid
|
|
tracing::info!("Event ingested: channel={id} kind={kind}");
|
|
```
|
|
|
|
### Commit Messages
|
|
|
|
Follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
|
|
```
|
|
feat(mcp): add get_feed_actions tool
|
|
fix(auth): reject expired NIP-42 challenges
|
|
docs(agents): document workflow MCP tools
|
|
refactor(db): extract channel queries into channel.rs
|
|
test(workflow): add approval gate integration test
|
|
```
|
|
|
|
The type prefix (`feat`, `fix`, `docs`, `refactor`, `test`, `chore`) is
|
|
required. The scope (in parentheses) is optional but encouraged.
|
|
|
|
---
|
|
|
|
## Making a Pull Request
|
|
|
|
### What a Good PR Looks Like
|
|
|
|
1. **Focused** — one logical change per PR. If you're fixing a bug and
|
|
refactoring a module, split them into two PRs.
|
|
|
|
2. **Tested** — new behavior has tests. Bug fixes include a regression test.
|
|
If a test is impractical, explain why in the PR description.
|
|
|
|
3. **Documented** — public APIs, new event kinds, new MCP tools, and new
|
|
config variables are documented. Update `README.md`, `AGENTS.md`, or
|
|
`VISION.md` as appropriate.
|
|
|
|
4. **CI passing** — `just ci` passes locally before you push.
|
|
|
|
5. **Clear description** — the PR description explains:
|
|
- What problem this solves (or what feature it adds)
|
|
- How it was implemented (key decisions, trade-offs)
|
|
- How to test it manually (if applicable)
|
|
- Any follow-up work deferred to a future PR
|
|
|
|
6. **Shows the UI** — any PR that changes the desktop or mobile UI includes
|
|
before/after screenshots (or a short recording for interactions) in the
|
|
description. We can't run every branch locally — screenshots let us review
|
|
UI changes same-day instead of waiting for someone to build your branch.
|
|
|
|
### PRs We're Unlikely to Merge
|
|
|
|
Some kinds of PRs usually get closed — not because they're bad ideas, but
|
|
because we can't safely review them without prior discussion:
|
|
|
|
- **Large refactors or dependency swaps** without a prior issue agreeing on
|
|
the direction
|
|
- **Cosmetic renames or style-only churn** that doesn't fix a bug or improve
|
|
clarity
|
|
- **Entirely new features** with no prior discussion
|
|
- **Drive-by changes bundled into an unrelated fix** — split them out
|
|
|
|
If you're considering any of these, open an issue first and we'll tell you
|
|
quickly whether it's a direction we'd merge. That saves your time as much as
|
|
ours.
|
|
|
|
### What to Expect After You Open a PR
|
|
|
|
- Maintainers triage new PRs on a best-effort cadence. Focused PRs that
|
|
follow this guide move fastest.
|
|
- Duplicates and PRs that skip this guide may be closed with a pointer here
|
|
rather than a full review. A close isn't a rejection of you or the idea —
|
|
address the gaps and reopen (or open a fresh PR) anytime.
|
|
- Address review comments by pushing new commits (don't force-push during
|
|
review; it makes it hard to see what changed).
|
|
- Once approved, a maintainer will squash-merge your PR.
|
|
|
|
---
|
|
|
|
## Architecture Overview
|
|
|
|
See [ARCHITECTURE.md](ARCHITECTURE.md) for the full system design and
|
|
[AGENTS.md](AGENTS.md#repo-structure) for the complete crate map. The key
|
|
design principles:
|
|
|
|
**The relay is the single source of truth.** All state flows through the
|
|
event store. Crates communicate through the database and Redis pub/sub — not
|
|
through direct function calls across crate boundaries (with the exception
|
|
of `buzz-core` types, which are shared everywhere).
|
|
|
|
**Event kinds are the only switch.** Every action in the system — a message,
|
|
a reaction, a workflow step, a canvas update — is a Nostr event with a kind
|
|
integer. Adding a new feature means defining a new kind. No breaking changes
|
|
to existing clients.
|
|
|
|
---
|
|
|
|
## Ecosystem
|
|
|
|
Buzz is developed across multiple repositories. This repo (`block/buzz`)
|
|
is the open-source home for all application code — the relay, desktop app,
|
|
mobile app, CLI, and agent harness. Internal repositories handle
|
|
enterprise-signed builds and infrastructure deployment.
|
|
|
|
See [AGENTS.md § Ecosystem](AGENTS.md#ecosystem) for the full repo table and
|
|
dependency diagram.
|
|
|
|
**External contributors:** Fork `block/buzz`, open a PR, and CI runs
|
|
automatically. No special access is required.
|
|
|
|
**Block team members:** See the internal
|
|
[sprout-releases CONTRIBUTING.md](https://github.com/squareup/sprout-releases/blob/main/CONTRIBUTING.md)
|
|
for team access setup, onboarding, and the full repo inventory. See
|
|
[RELEASING.md](RELEASING.md) for the release process.
|
|
|
|
---
|
|
|
|
## How to Add a New Event Kind
|
|
|
|
1. **Define the kind constant** in `buzz-core/src/kind.rs`:
|
|
|
|
```rust
|
|
/// My new event kind — description of what it represents.
|
|
pub const KIND_MY_FEATURE: u32 = 4XXXX;
|
|
```
|
|
|
|
Pick a kind number in the appropriate sub-range defined in `kind.rs`.
|
|
Check the `ALL_KINDS` array for collisions. Each sub-range is documented
|
|
with comments in the file.
|
|
|
|
2. **Define the payload type** in the appropriate module in `buzz-core/src/`
|
|
(e.g., alongside `event.rs`) if the content field is structured JSON:
|
|
|
|
```rust
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct MyFeaturePayload {
|
|
pub field_one: String,
|
|
pub field_two: Option<u64>,
|
|
}
|
|
```
|
|
|
|
3. **Register the kind's required scope** in
|
|
`crates/buzz-relay/src/handlers/ingest.rs` inside
|
|
`required_scope_for_kind()`. This controls which auth scope a caller
|
|
needs to submit the event:
|
|
|
|
```rust
|
|
KIND_MY_FEATURE => Ok(Scope::MessagesWrite),
|
|
```
|
|
|
|
4. **Handle post-storage side effects** by adding a match arm in
|
|
`crates/buzz-relay/src/handlers/side_effects.rs` inside
|
|
`handle_side_effects()`:
|
|
|
|
```rust
|
|
KIND_MY_FEATURE => handle_my_feature(event, state).await?,
|
|
```
|
|
|
|
`handle_side_effects()` runs after the event is stored — use it for
|
|
notifications, cache invalidation, or derived data. If the new kind
|
|
also needs an HTTP bridge surface (for example, a protocol helper that
|
|
cannot practically use WebSocket), add a handler in
|
|
`crates/buzz-relay/src/api/` and register it in
|
|
`crates/buzz-relay/src/router.rs`.
|
|
|
|
5. **Persist to the database** — if the event needs to be queryable, add a
|
|
handler in `buzz-db/src/` (e.g., `buzz-db/src/my_feature.rs`) with
|
|
the appropriate `INSERT` and `SELECT` queries.
|
|
|
|
6. **Index for search** (if applicable) — Postgres FTS indexes persisted
|
|
events automatically via the `events.search_tsv` generated column. To
|
|
exclude a privacy-sensitive kind from search, add it to the `CASE WHEN
|
|
kind IN (...)` exclusion in the `search_tsv` definition (see the initial
|
|
schema migration) rather than wiring a separate indexer.
|
|
|
|
7. **Audit** — the audit log captures all events automatically; no changes
|
|
needed unless you need custom audit metadata.
|
|
|
|
8. **Write tests** — add a unit test for payload serialization in
|
|
`buzz-core` and an integration test in `buzz-test-client` that sends
|
|
the new event kind and verifies the expected behavior.
|
|
|
|
9. **Document** — `kind.rs` is the authoritative registry of all kind numbers.
|
|
Update `README.md` if it's a user-facing feature.
|
|
|
|
---
|
|
|
|
## How to Add a New API Endpoint
|
|
|
|
Prefer a signed Nostr event and the existing WebSocket/`POST /events` ingest
|
|
path over adding endpoint-specific JSON APIs. The relay intentionally exposes
|
|
only a narrow HTTP surface: NIP-11/NIP-05 metadata, `/events`, `/query`,
|
|
`/count`, `/hooks/{id}`, Blossom media, git smart HTTP, git policy hooks, and
|
|
health probes.
|
|
|
|
If an HTTP endpoint is still necessary:
|
|
|
|
1. **Define the handler** in the appropriate module under
|
|
`crates/buzz-relay/src/api/`. Resolve the request tenant before any auth or
|
|
data lookup, use NIP-98 when the endpoint accepts user credentials, and keep
|
|
community scoping explicit.
|
|
|
|
2. **Register the route** in `crates/buzz-relay/src/router.rs` using the
|
|
narrowest path possible. Do not add new `/api/*` compatibility routes unless
|
|
the product decision explicitly calls for one.
|
|
|
|
3. **Add database queries** in `buzz-db/src/` only when the endpoint cannot be
|
|
expressed through the existing event query paths.
|
|
|
|
4. **Handle errors** using the `api_error()`, `internal_error()`, and
|
|
`not_found()` helpers in `buzz-relay/src/api/mod.rs`. Return
|
|
`(StatusCode, Json<Value>)` tuples.
|
|
|
|
5. **Write tests** with the `buzz-test-client` harness in
|
|
`crates/buzz-test-client/tests/`, covering auth, community scoping, and the
|
|
relevant success path.
|
|
|
|
6. **Document** any public endpoint in `ARCHITECTURE.md` and user-facing docs.
|
|
|
|
---
|
|
|
|
## License and CLA
|
|
|
|
Buzz is licensed under the **Apache License, Version 2.0**. See
|
|
[LICENSE](LICENSE) for the full text.
|
|
|
|
By submitting a pull request, you agree that your contribution is licensed
|
|
under the Apache 2.0 license and that you have the right to submit it.
|
|
|
|
If your employer has rights to intellectual property you create, you may need
|
|
their sign-off. When in doubt, check with your legal team.
|
|
|
|
---
|
|
|
|
*Thank you for contributing to Buzz. Every bug report, documentation fix,
|
|
and code contribution makes the project better for everyone. 🐝*
|