Files
buzz/benchmarks/harbor-buzz-orchestra
Matt Toohey 8216db1c71 chore(toolchain): bump Rust to 1.98.1 for macOS 27 proc-macro loading (#8598)
## Summary

Bump the pinned Rust toolchain from 1.95.0 to 1.98.1 so release builds
work on macOS 27 hosts.

Rust ≤ 1.97 strips Apple-target artifacts with its bundled
`llvm-objcopy`, which leaves the Mach-O `LC_SYMTAB` string pool
(`__LINKEDIT`) only 4-byte aligned. macOS 27's dyld refuses to `dlopen`
such images (`mis-aligned LINKEDIT string pool`). Because cargo applies
the release profile's default `strip = "debuginfo"` to proc-macro dylibs
too, `cargo build --release` of any crate that uses sqlx macros
(`buzz-db`, `buzz-relay`, `buzz-admin`, …) fails on macOS 27: rustc
cannot load `libsqlx_macros-*.dylib`. Dev builds pass because the dev
profile does not strip, which is why this only shows up as a
release/packaging failure.

The fix (llvm/llvm-project#203680, pulled into rustc by
rust-lang/rust#158410 for rust-lang/rust#157750) first shipped in Rust
1.98.0 and was never backported to 1.97.x. `block/buzz-app` pinned
1.98.1 for the same failure in block/buzz-app#529. With this bump the
`CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_STRIP=none` / custom-linker
workarounds people have been using locally are no longer needed.

### What changes

Two commits: the toolchain bump (with the desktop clippy fixes), then
the workspace clippy fixes.

| Location | Change |
|---|---|
| `rust-toolchain.toml` | `channel = "1.95.0"` → `"1.98.1"` (profile
unchanged) |
| `Dockerfile`, `Dockerfile.push-gateway` | `ARG RUST_VERSION=1.95` →
`1.98` |
| `Dockerfile.sprig` | `rust:1.98-alpine3.22` with the manifest-list
digest verified on Docker Hub (shared by the `1.98` and `1.98.1` tags) |
| `.github/workflows/release.yml` | `dtolnay/rust-toolchain` pinned to
the tip of its `1.98.1` branch (`18f04874`) for the Windows desktop
release job |
| `benchmarks/harbor-buzz-orchestra` | `RUST_IMAGE = rust:1.98-alpine` |
| `_ci-rust.yml`, `windows-canary.yml`, `app_state_tests.rs` | stale
"1.95" comments reworded; the `unknown_lints` allow stays with a
version-agnostic rationale |
| `desktop/src-tauri` | lints clippy 1.98 newly raises under `-D
warnings`: `chunks_exact_to_as_chunks` at five fixed-width PCM/UTF-16
decoders, `needless_borrows_for_generic_args` on the `Copy` `norm`
closure in `config_bridge/reader.rs`, and a redundant `Engine as _`
import in `hpke_key_backup_tests.rs` that `use super::*` already
provides |
| Workspace crates (9) | the 30 lints clippy 1.98 newly raises in `cargo
clippy --workspace --all-targets -- -D warnings`, listed below |

Workspace lint fixes:

| Lint | Sites | Fix |
|---|---|---|
| `result_large_err` (20) | `buzz-relay` `api/git/transport.rs` (8),
`api/git/settings.rs` (6), `api/gifs.rs`, `api/workflows.rs`,
`api/profiles.rs`; `buzz-push-gateway` `verify_installation_assertion` |
Clippy 1.98 now checks `async fn`s, so every `async fn … -> Result<_,
axum::response::Response>` trips it. These follow the existing allow
pattern in `api/media.rs` and `nip_fi_http.rs` ("Response is the natural
error type for axum handlers"): module-level in the two git files,
per-function elsewhere. Boxing `Response` would touch every handler for
no runtime benefit. |
| `chunks_exact_to_as_chunks` (3) | `buzz-media` MP4 brand scan;
`buzz-voice` npy f32 reader and stereo test fixture | `chunks_exact(N)`
→ `as_chunks::<N>().0` |
| `question_mark` (3) | `buzz-acp` queue flush fallback and
slash-command mention skip; `buzz-agent` `find_git_root` | `match … {
None => return None }` → `?` |
| `useless_format` (2) | `buzz-auth` nip_fi startup test policies |
`format!("…")` → `"…".to_string()` |
| `useless_borrows_in_formatting` (1) | `buzz-pairing-cli` secret print
| drop the borrow |
| `manual_noop_waker` (1) | `buzz-sdk` broker test `block_on` |
`Waker::noop()` (stable since 1.85) |

All `as_chunks` rewrites keep `chunks_exact`'s remainder-dropping
semantics, and `as_chunks` is stable since 1.88, which is the MSRV.

One change in `api/git/transport.rs` is not a lint fix. Staging that
file trips the pre-commit secret scanner on its existing test-only ES256
fixture (`GIT_TEST_EC_PEM`), and the scanner only honours a suppression
marker on the flagged line. So the `\`-continued string literal is
rewritten as a byte-identical `concat!` whose `BEGIN` line carries
`sadscan:disable kingfisher.privkey.2`.

Left as is on purpose: `Cargo.toml` `rust-version = "1.88.0"` (MSRV
floor, a separate concern), the README/CONTRIBUTING "1.88+" text, Hermit
(no Rust packages there), the lockfiles, and the `(1.95.0)` comment in
`scripts/start-isolated-test-relay.sh` (the pre-commit secret scanner
flags that file's pre-existing dev-only `DATABASE_URL` whenever it is
staged).

### Related issue
Fixes #8074

### Testing
All under rustc/clippy 1.98.1 on a macOS 27 host (Darwin 27.0.0,
aarch64), with no `RUSTFLAGS` / `SDKROOT` / strip overrides in the
environment:

- `cargo build --release -p buzz-db` (the #8074 repro: the release
profile strips the proc-macro dylib): **builds**. The resulting
`libsqlx_macros-*.dylib` (14 MB) has `LC_SYMTAB stroff % 8 == 0` per
`otool -l`. The same build on 1.95.0 died with `mis-aligned LINKEDIT
string pool`.
- `just clippy` (workspace, `-D warnings`) and `cargo fmt --check`
(workspace and `desktop/src-tauri`): pass
- `just desktop-tauri-clippy` (default and `mesh-llm` feature sets):
pass
- `just test-unit`: every crate passes. The `buzz-acp` binary failed
only under pre-push lane contention; rerun solo it is 1060 passed, 0
failed.
- `just desktop-tauri-test`: 3422 passed, 0 failed
- After the lint fixes: lib/bin unit tests for `buzz-acp`, `buzz-agent`,
`buzz-auth`, `buzz-media`, `buzz-voice`, `buzz-sdk`, `buzz-pairing-cli`,
and `buzz-relay`'s `api::git::transport` tests, including the
Postgres-backed
`enforce_mode_git_pack_routes_missing_assertion_exact_bytes`, which
parses the rewritten PEM: pass

`cfg(windows)` code was not linted locally (no Windows target
installed). CI's Windows Rust job runs workspace clippy for
`x86_64-pc-windows-msvc`, and that passes on this branch. CI also builds
the relay, push-gateway and sprig images on the new `rust:1.98` bases
for amd64 and arm64.

No runtime behavior changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-27 08:13:38 +11:00
..

Harbor Buzz Orchestra

A stock-Harbor custom agent that runs a manifest-defined team through the real Buzz stack. Harbor sees one BuzzOrchestraAgent; behind that adapter, one orchestrator and N workers coordinate over the production relay/Postgres. Each agent runs inside the Harbor task container as the same buzz-acp → buzz-agent → buzz-dev-mcp process tree the desktop app launches: the production MCP toolset (shell, file tools, todo) with the buzz CLI on the shell's PATH. No Harbor fork or patch is required.

Define the team

The manifest is the benchmark condition. Each roster entry selects an agent class's count, model endpoint, byte-pinned system prompt, generation settings, and budget:

condition: my-team
roster:
  - id: orch
    kind: orchestrator
    role: lead
    count: 1
    endpoint: databricks/frontier
    prompt: {path: personas/orchestrator.md, sha256: <sha256>}
    generation: {max_output_tokens: 4096, context_window_tokens: 128000}
  - id: worker
    kind: worker
    role: implementer
    count: 4
    endpoint: databricks/fast-worker
    prompt: {path: personas/worker.md, sha256: <sha256>}
    generation: {max_output_tokens: 4096, context_window_tokens: 128000}

endpoint_config maps those endpoint names to providers, URLs, and API-key environment variables. The adapter contains no fixed roster or model.

Run

With the production compose stack and model endpoints already running, execute one task (-p), a directory of tasks, or replace -p with Harbor's dataset and task selectors:

uv run --project benchmarks/harbor-buzz-orchestra/testbed harbor run --yes -p <TASK_OR_DIRECTORY> --agent harbor_buzz_orchestra:BuzzOrchestraAgent --agent-kwarg manifest=<CONDITION.yaml> --agent-kwarg provisioner_factory=harbor_buzz_testbed:provisioner_from_dict --agent-kwarg provisioner_config=<PROVISIONER.json> --agent-kwarg endpoint_config=<ENDPOINTS.json> --agent-kwarg artifact_root=benchmarks/harbor-buzz-orchestra --agent-kwarg buzz_acp_binary=<LINUX_BIN>/buzz-acp --agent-kwarg buzz_agent_binary=<LINUX_BIN>/buzz-agent --agent-kwarg buzz_dev_mcp_binary=<LINUX_BIN>/buzz-dev-mcp --agent-kwarg buzz_cli_binary=target/debug/buzz --agent-kwarg run_id="bench-$(date -u +%Y%m%dT%H%M%SZ)" --agent-timeout-multiplier 15 --n-concurrent 1

buzz_acp_binary/buzz_agent_binary/buzz_dev_mcp_binary must be Linux builds matching the task image architecture — they are uploaded into each task container (just benchmark cross-builds them automatically; musl-static, so any Linux base image works). buzz_cli_binary is the host CLI the harness uses to act as the trial user.

--n-concurrent 1 is the safe laptop setting for a serialized local model; it is not an orchestration requirement. Some TB graders install dependencies from public package registries at verification time — run benchmarks off networks that block those installs (e.g. corporate VPNs).

Each trial gets fresh keys and a private Buzz channel. The provisioner archives rather than deletes that channel, leaving the relay/Postgres event timeline and the per-agent acp/agent logs (downloaded into the trial's buzz/ artifacts) available for analysis.

Buzz-native tasks

The local benchmarks/buzz-dataset suite — a sibling directory of this harness, not a subdirectory of it — scores Buzz product behavior alongside task correctness. It covers direct thread replies, callback user mentions, targeted reads of named paths, exact channel membership, multiline delivery, non-waking narrative names, batched reports, cross-thread isolation, ambiguous identities, and explicit cold-memory retrieval. Run one task with the production base prompt from the checked-out source build:

just benchmark \
  --path benchmarks/buzz-dataset/reply-to-thread \
  --manifest benchmarks/harbor-buzz-orchestra/manifests/buzz-native-solo-luna.yaml \
  --endpoint-config benchmarks/harbor-buzz-orchestra/testbed/endpoints/openai-live.json \
  --n-concurrent 1

Buzz-native tasks declare one of two evaluation layers in task.toml. Regression tasks are deterministic product/prompt regression checks and default to k=1. Workflow tasks exercise multi-step collaboration capabilities and default to k=3 (not 5). Run a layer by metadata with --path benchmarks/buzz-dataset --layer regression or --layer workflow; task identities stay unchanged.

When the Buzz dataset root is passed without --layer or --attempts, the wrapper starts two sequential Harbor jobs so each layer gets its own default. A direct task path infers its layer's default. An explicit --attempts/-k overrides the defaults and permits one mixed-layer job. Terminal-Bench and other unrelated paths keep their existing k=5 default.

The default condition is buzz-native-solo-luna.yaml — one solo agent on gpt-5.6-luna at thinking_effort: medium. What this suite scores comes from the base prompt rather than from model strength, so the cheap model at a middling effort is the right yardstick: a weak result here is a prompt finding, not a model finding. It needs OPENAI_COMPAT_API_KEY and the explicit --endpoint-config above, because --endpoint-config defaults to anthropic-live.json. Swap in buzz-native-solo-sonnet.yaml (no --endpoint-config, needs ANTHROPIC_API_KEY) to compare against Sonnet 4.6.

A roster entry that does not pin generation.thinking_effort runs at the runtime default (THINKING_EFFORT, currently medium) rather than at whatever the provider defaults to, so the level is always recorded. Leaving it unset does not change a condition's hash — manifests written before the effort axis existed keep their identity and stay comparable to their earlier receipts.

Replace the path with benchmarks/buzz-dataset/create-channel-invite-users to run the channel task. Its provisioner seeds a stable directory of 50 users and 10 bots, while the verifier checks the created channel's TTL and exact membership through post-agent CLI evidence.

After the agent stops, the runtime snapshots public relay state (source messages plus any task-declared channels and members) to /logs/artifacts/buzz-evidence.json. The task verifier reads that post-agent artifact; relay credentials and database access are never exposed to the model or verifier. If the snapshot cannot be exported the trial fails rather than scoring 0 — a harness fault and a model fault stay distinguishable — and the cause is written to the trial's buzz/buzz-evidence-error.txt.

Some tasks declare additional signed relay events. The provisioner creates their actors as normal channel identities and the runtime publishes the events through the production CLI immediately after the task message. Evidence exports only public actor metadata and event IDs; their signing credentials never enter the task container or verifier artifact.

Each task ships its own README.md documenting its reward dimensions and, for the tasks whose graded Buzz behavior is deliberately absent from instruction.md (reply-to-thread, user-mention), why that omission is the point. Read it before editing a task's instruction or verifier.

Leaderboard runs

just benchmark is the one-command path: it stands up a dedicated Docker stack (buzz-benchmark compose project — relay :3600, Postgres :5633, secrets generated once into the gitignored .benchmark/), applies the benchmark schema, and defaults to leaderboard-eligible settings (Terminal-Bench 2.1, 5 attempts per problem, the Sonnet+Haiku team). All selectors pass through:

just benchmark                                   # full TB 2.1, k=5
just benchmark --path <TASK_DIR> -k 1            # one local task, one attempt
just benchmark --path benchmarks/buzz-dataset --layer regression   # Buzz k=1
just benchmark --path benchmarks/buzz-dataset --layer workflow     # Buzz k=3
just benchmark -i "cobol*" --attempts 3          # dataset subset
just benchmark --gui                             # watch the run live

One pinned user identity fronts the whole benchmark environment: it owns every trial channel (named after the task) and posts every task prompt, and trial channels are kept rather than archived. --gui adds that user to the relay membership list and opens the Buzz desktop app logged in as them, so channels fill the sidebar as the run progresses — watch, don't type; a human message mid-trial would taint the run. just benchmark-down stops the stack.

Networking: the relay is host-header tenant-bound, so agents must dial its canonical address (ws://localhost:3600) even from inside a task container. just benchmark uploads a tiny std-only loopback forwarder (forwarder/relay_forwarder.rs) with the agent stack; it listens on the container's loopback and bridges the byte stream to the Docker host gateway (host.docker.internal, overridable via BUZZ_BENCHMARK_DOCKER_HOST).

scripts/run_leaderboard.py is the layer underneath, for running against an already-provisioned stack. It wraps the invocation above with only leaderboard-legal settings — it does not accept or forward timeout or resource overrides, so the job directory it produces passes Harbor's static validation as-is. Give it a problem set, attempts per problem, and a team manifest:

uv run --project benchmarks/harbor-buzz-orchestra/testbed \
    benchmarks/harbor-buzz-orchestra/scripts/run_leaderboard.py \
    --dataset terminal-bench/terminal-bench-2-1 \
    --attempts 5 \
    --manifest benchmarks/harbor-buzz-orchestra/manifests/<TEAM>.yaml \
    --endpoint-config benchmarks/harbor-buzz-orchestra/testbed/endpoints/<ENDPOINTS>.json \
    --provisioner-config <PROVISIONER.json>

--path replaces --dataset for local task directories; --include-task / --exclude-task filter by glob; --dry-run prints the underlying harbor run command. After the job finishes the script derives a metadata.yaml from the manifest roster (validated schema; review the display names before submitting) and prints the harbor upload / harbor leaderboard submit commands.

Validate

cd benchmarks/harbor-buzz-orchestra
uv run --extra dev pytest -q
uv run --extra dev ruff check .
cd testbed
uv run --extra dev pytest -q
uv run --extra dev ruff check .

Live provisioner tests require the benchmark compose stack and opt-in environment described in testbed/tests/test_provisioner_live.py.