Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
2527efa
feat(buzz-agent-core): drive buzz-agent's ACP surface on the goose li…
michaelneale Jul 27, 2026
63efbb2
feat(buzz-agent-core): pin goose by git rev, wire session/set_model
michaelneale Jul 27, 2026
4612452
fix(buzz-agent-core): advertise the model catalog on session/new
michaelneale Jul 27, 2026
e9aca45
feat(buzz-agent-core): implement the _Stop veto and _PostCompact re-i…
michaelneale Jul 27, 2026
22f5e70
feat(buzz-agent-core): inject [Reflect] on failed tool results
michaelneale Jul 27, 2026
737bde4
test(buzz-agent-core): validate against the real buzz-dev-mcp
michaelneale Jul 27, 2026
b631552
fix(buzz-agent-core): drain on cancel instead of dropping the stream
michaelneale Jul 27, 2026
fbe120f
test(buzz-agent-core): cover session/steer, the last untested ACP method
michaelneale Jul 27, 2026
3596e8e
fix(buzz-agent-core): correct a false claim about GooseMode::default()
michaelneale Jul 27, 2026
c12853e
build(buzz-agent-core): emit the binary as `buzz-agent`
michaelneale Jul 27, 2026
1bd2d94
docs(buzz-agent-core): add HANDTEST.md and a `just agent-core` recipe
michaelneale Jul 27, 2026
1f6b75a
refactor(buzz-agent): swap the agent loop onto the goose library
michaelneale Jul 27, 2026
ac6aed1
docs(buzz-agent): rewrite HANDTEST.md for the swap
michaelneale Jul 27, 2026
81eec0f
fix(buzz-agent): restore `auth` subcommand and close config compat gaps
michaelneale Jul 27, 2026
43d3698
feat(buzz-agent): restore relay-mesh adaptive MoA routing
michaelneale Jul 27, 2026
55696fc
docs(buzz-agent): pin the one mesh behaviour that is NOT parity
michaelneale Jul 28, 2026
41974bc
fix(buzz-agent): catch mesh-llm's 502 MoA failure path, not just the 503
michaelneale Jul 28, 2026
6c14563
Merge remote-tracking branch 'origin/main' into micn/buzz-agent-goose…
michaelneale Jul 28, 2026
fa8166e
fix(buzz-agent): address expert review — shutdown, cap race, usage model
michaelneale Jul 28, 2026
cca05c0
feat(buzz-agent): restore AGENTS.md hints and load_skill on the goose…
michaelneale Jul 28, 2026
66d1bff
chore(deny): allow MIT-0 for goose's borrow-or-share transitive dep
michaelneale Jul 28, 2026
3a7ffc0
refactor(buzz-agent): dispatch load_skill as a platform extension, no…
michaelneale Jul 31, 2026
1dee392
Merge origin/main into micn/buzz-agent-goose-core
michaelneale Jul 31, 2026
d35f704
Merge origin/main into buzz-agent-goose-core
michaelneale Aug 11, 2026
80a9f9a
chore(buzz-agent): bump goose pin to bf332b9 (unrolled loop) and fix …
michaelneale Aug 11, 2026
9869f24
refactor(buzz-agent): buzz owns the agent loop; goose supplies the parts
michaelneale Aug 11, 2026
1d098d6
test(buzz-agent): hand-test script driving a real provider over ACP s…
michaelneale Aug 11, 2026
20a731b
fix(desktop): admit buzz_agent logs to per-agent log files
michaelneale Aug 11, 2026
626b079
fix(buzz-agent): keep agent conversations out of the user's goose ses…
michaelneale Aug 11, 2026
c918877
fix(buzz-agent): stop rendering every AGENTS.md into the prompt twice
michaelneale Aug 11, 2026
c312a70
fix(buzz-agent): mark steered messages with with_steer()
michaelneale Aug 11, 2026
979eff9
refactor(buzz-agent): express the round budget as a goose Operation
michaelneale Aug 11, 2026
ce84471
refactor(buzz-agent): keep the turn conversation in memory, not sqlite
michaelneale Aug 11, 2026
45584a9
refactor(buzz-agent): hold the provider on buzz's side, not in goose'…
michaelneale Aug 11, 2026
6c7648f
fix(deps): patch eight nostr advisories instead of ignoring them
michaelneale Aug 11, 2026
3190102
merge origin/main into micn/buzz-agent-goose-core-v2
michaelneale Aug 11, 2026
8e1ba34
refactor(buzz-agent): use goose's skills instead of buzz's duplicate
michaelneale Aug 12, 2026
69c6091
feat(buzz-agent): back goose's Databricks provider with buzz's PKCE e…
michaelneale Aug 12, 2026
42fb755
fix(buzz-agent): the agent's configured provider must beat an inherit…
michaelneale Aug 12, 2026
d4db850
fix(desktop): stop an agent inheriting the developer's provider
michaelneale Aug 12, 2026
15f6fd5
refactor(buzz-agent): express the _Stop veto as a goose Operation
michaelneale Aug 12, 2026
ff44cf4
fix(desktop): keep runtime.rs under the file-size ratchet
michaelneale Aug 12, 2026
ab5b289
fix(buzz-agent): restore the env surface main had
michaelneale Aug 12, 2026
aef3921
fix(desktop): strip inherited agent config by prefix, not by name
michaelneale Aug 12, 2026
099bacb
refactor(buzz-agent): express steer and compaction as goose Operations
michaelneale Aug 12, 2026
60e63a0
docs(buzz-agent): record why [Reflect] stays out of the state machine
michaelneale Aug 12, 2026
e0d5f20
test(buzz-agent): cover the steer and compaction operations
michaelneale Aug 12, 2026
0ea48f4
test(buzz-agent): prove turn-to-turn memory across separate prompts
michaelneale Aug 12, 2026
9ca8806
Merge origin/main into micn/buzz-agent-goose-core
michaelneale Aug 18, 2026
e5f73cd
fix(buzz-agent): carry BUZZ_AGENT_LLM_TIMEOUT_SECS onto goose
michaelneale Aug 18, 2026
16a5282
docs(buzz-agent): correct stale env/limit rows after the goose move
michaelneale Aug 18, 2026
678dc68
chore(buzz-agent): drop the resurrected model_capabilities.rs copy
michaelneale Aug 18, 2026
b3d77f2
test(buzz-agent): cover provider failure paths end-to-end
michaelneale Aug 18, 2026
01c9137
Merge origin/main into micn/buzz-agent-goose-core
Aug 20, 2026
913cd56
chore(buzz-agent): update goose to 1.47
Aug 20, 2026
55edc4a
fix(agent): preserve migration behavior
Aug 21, 2026
a0dd2d4
Merge origin/main into micn/buzz-agent-goose-core
Aug 21, 2026
2ea414f
test(agent): serialize provider environment
Aug 21, 2026
e0d7899
Merge current main and update Goose GDK
Aug 24, 2026
91c2f01
Merge remote-tracking branch 'origin/main' into micn/buzz-agent-goose…
michaelneale Aug 25, 2026
a41b78f
Merge origin/main into micn/buzz-agent-goose-core
michaelneale Aug 26, 2026
0a4c78e
fix(buzz-agent): keep goose's bundled skills out of Buzz agents
michaelneale Aug 26, 2026
55326e3
refactor(buzz-agent): use goose-agent loop APIs directly
Aug 27, 2026
53692af
Merge remote-tracking branch 'origin/main' into jimmy/gdk-direct-goos…
Aug 27, 2026
04a10f6
Merge origin/main and update Goose GDK
Aug 31, 2026
9344b73
Merge current main into GDK branch
Aug 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
3,863 changes: 2,886 additions & 977 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"crates/buzz-audit",
"crates/buzz-acp",
"crates/buzz-agent",
"crates/buzz-model-catalog",
"crates/sprig",
"crates/buzz-test-client",
"crates/buzz-ws-client",
Expand Down
103 changes: 23 additions & 80 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -316,20 +316,12 @@ test-unit:
./scripts/test-ensure-local-relay-key.sh
if command -v cargo-nextest &>/dev/null; then
cargo nextest run -p buzz-core -p buzz-auth --lib
# buzz-auth NIP-FI verifier doctests. The sealed-authority
# `compile_fail` doctests prove the default-feature public API alone
# cannot forge the issuer→JWKS authority; nextest does not run
# doctests, hence this separate step. The verifier's regression suite
# lives in the in-crate `#[cfg(test)] mod tests`, so `--lib` above
# already runs it.
cargo test -p buzz-auth --doc
cargo nextest run -p buzz-voice --lib
cargo nextest run -p buzz-cli
# buzz-db migrator/lint tests: pure SQL-parsing unit tests (no infra).
# They guard the embedded-migrator invariant (the complete checked-in
# additive migration set; legacy cutover/backfill remains an operator
# script, not startup state) and the tenant-scoping lints. The
# Postgres-backed buzz-db tests are
# They guard the embedded-migrator invariant (exactly the consolidated
# 0001; cutover/backfill stays an operator script, not startup state)
# and the tenant-scoping lints. The Postgres-backed buzz-db tests are
# #[ignore]d, so --lib runs only the infra-free set. Without this gate a
# stray file in migrations/ or a broken lint ships green.
cargo nextest run -p buzz-db --lib
Expand All @@ -347,39 +339,21 @@ test-unit:
# because nothing in CI runs `cargo test --workspace` — workspace
# membership alone buys clippy/check, not a single executed test.
cargo nextest run -p buzz-backend-kubernetes
# buzz-agent model-capabilities corpus: the Rust half of the
# cross-language drift guard. `model_capabilities.rs` embeds
# scripts/model-capabilities.json + scripts/normative-corpus.json via
# include_str! and replays the full locked corpus as pure in-process tests (no
# infra). Enumerated explicitly because nothing in CI runs
# `cargo test --workspace`; without this step a manifest edit that
# diverges Rust from the corpus ships green.
cargo nextest run -p buzz-agent --lib
# Admin API auth-boundary tests (api::admin in buzz-relay): the NIP-98
# duplicate-tag rejections, the Host/Origin replay-ordering causal pair,
# the admin.localhost origin/advertisement/canonical-URL pins, and the
# host-oracle/credential-first checks. These are the regression guard for
# the /api/admin/v1 moderation auth surface. Enumerated explicitly because
# nothing in CI runs `cargo test --workspace`, `just test-unit` did not
# enumerate `buzz-relay --lib`, and Backend Integration selects only the
# #[ignore]d Postgres suites — so these non-ignored tests ran in no lane
# and a red one could ship green (exactly how a broken admin test slipped
# past every gate once). Scoped to api::admin, not the whole buzz-relay
# --lib, because api::media has non-ignored tests that require Postgres.
# Two api::admin tests are excluded: both exercise a read-route DB
# fallthrough and pass without a database only by waiting out the sqlx
# acquire timeout (~30s each), so they do not belong in the infra-free
# unit job. nip98_mode_unrostered_signer_does_not_consume_a_replay_slot
# asserts a unique replay-guard invariant, so it is wired into the
# Postgres-backed Backend Integration job (see ci.yml "Admin API
# unrostered-signer replay invariant"). disabled_mode_allows_
# unauthenticated_requests_on_the_admin_host has no unique invariant:
# disabled-mode unauthenticated success is covered by
# disabled_mode_regression_pin_unauthenticated_request_is_served on the
# DB-free /probe route, and its Host/Origin gating is covered here by
# disabled_mode_still_requires_the_correct_host / _a_matching_origin.
cargo nextest run -p buzz-relay --lib \
-E 'test(/^api::admin::/) - test(=api::admin::tests::disabled_mode_allows_unauthenticated_requests_on_the_admin_host) - test(=api::admin::tests::nip98_mode_unrostered_signer_does_not_consume_a_replay_slot)'
# model-capabilities corpus: the Rust half of the cross-language drift
# guard. `model_capabilities.rs` embeds scripts/model-capabilities.json
# + scripts/normative-corpus.json via include_str! and replays all 103
# vectors as pure in-process tests (no infra). It lives in
# buzz-model-catalog rather than buzz-agent because the desktop reads
# capabilities without linking goose. Enumerated explicitly because
# nothing in CI runs `cargo test --workspace`; without this step a
# manifest edit that diverges Rust from the corpus ships green.
# buzz-agent's Buzz-owned loop contracts live in both src unit tests
# and infra-free tests/ integration tests (ACP stdio, cancellation,
# steering, permissions, hooks, skills). The real-dev-MCP tests launch
# the sibling binary directly, so build that fixture first.
cargo build -p buzz-dev-mcp
cargo nextest run -p buzz-agent
cargo nextest run -p buzz-model-catalog --lib
else
./scripts/run-tests.sh unit
fi
Expand All @@ -390,12 +364,12 @@ test-integration:

# Regenerate the model-capability normative corpus from the production Rust
# resolver. The corpus is a golden snapshot, never hand-edited: this runs the
# `#[ignore]`d writer test in buzz-agent, which serializes `resolve()` over the
# `#[ignore]`d writer test in buzz-model-catalog, which serializes `resolve()` over the
# inputs-only question table to scripts/normative-corpus.json. Run this after
# any model-capabilities.json edit, then commit the regenerated file. The
# `corpus_matches_generated_snapshot` gate fails CI if the committed file drifts.
regen-model-corpus:
cargo test -p buzz-agent --lib model_capabilities::tests::regen_corpus_file -- --ignored --exact
cargo test -p buzz-model-catalog --lib model_capabilities::tests::regen_corpus_file -- --ignored --exact

# Buzz shared compute e2e: current desktop discovery/admission logic and
# Playwright UI coverage.
Expand Down Expand Up @@ -480,7 +454,7 @@ relay-web: bootstrap _ensure-migrations
pnpm -C web build
BUZZ_WEB_DIR=./web/dist cargo run -p buzz-relay

# Build and run the private admin dashboard
# Build and run the private read-only admin dashboard
admin: bootstrap _ensure-migrations
#!/usr/bin/env bash
set -euo pipefail
Expand All @@ -492,26 +466,20 @@ admin: bootstrap _ensure-migrations
pnpm -C admin-web build
export BUZZ_ADMIN_HOST="${BUZZ_ADMIN_HOST:-admin.localhost:3000}"
export BUZZ_ADMIN_WEB_DIR="${BUZZ_ADMIN_WEB_DIR:-{{justfile_directory()}}/admin-web/dist}"
# Default to disabled auth locally: localhost is the network boundary and a
# NIP-07 signer extension can't be assumed in dev. Override per run with
# BUZZ_ADMIN_AUTH=nip98 (plus RELAY_OPERATOR_PUBKEYS or RELAY_OWNER_PUBKEY)
# to exercise the authenticated path.
export BUZZ_ADMIN_AUTH="${BUZZ_ADMIN_AUTH:-disabled}"
echo "Admin dashboard: http://${BUZZ_ADMIN_HOST}/reports"
echo "Auth mode: ${BUZZ_ADMIN_AUTH} (set BUZZ_ADMIN_AUTH=nip98 to require a signed operator)"
cargo run -p buzz-relay

# Seed deterministic reports and product feedback for local admin dashboard review
admin-seed: _ensure-migrations
./scripts/seed-admin-dashboard.sh

# Run focused relay and browser checks for the admin dashboard
# Run focused relay and browser checks for the read-only admin dashboard
admin-check: fmt-check
cargo check -p buzz-relay --all-targets
cargo test -p buzz-relay api::admin
cargo test -p buzz-relay router::tests
pnpm -C admin-web check
pnpm -C admin-web test:e2e
pnpm -C admin-web exec playwright test

# Start the relay server in release mode
relay-release: bootstrap _ensure-migrations
Expand Down Expand Up @@ -1059,31 +1027,6 @@ benchmark *ARGS:
uv run --project benchmarks/harbor-buzz-orchestra/testbed \
benchmarks/harbor-buzz-orchestra/scripts/benchmark.py {{ARGS}}

# Run the benchmark adapter + testbed gate exactly as CI does (pytest + ruff, pinned ruff from pyproject)
benchmark-check:
#!/usr/bin/env bash
set -euo pipefail
cd "{{justfile_directory()}}/benchmarks/harbor-buzz-orchestra"
# CI installs the dev extra with pip, so pyproject — not uv.lock — decides
# which ruff lints. Read the pin from there so this recipe cannot drift
# from the workflow (a floating specifier once meant CI failed on RUF100
# while the locked local ruff passed).
ruff_pin="$(grep -oE 'ruff==[0-9.]+' pyproject.toml | head -1 | cut -d= -f3)"
for project in . testbed; do
(
cd "$project"
echo "── harbor-buzz-orchestra/$project (ruff $ruff_pin)"
uv run --frozen pytest -q
uvx "ruff@$ruff_pin" check .
uvx "ruff@$ruff_pin" format --check .
)
done
# The task verifiers live in the sibling benchmarks/buzz-dataset, so they
# need the harness config passed explicitly to stay linted.
echo "── buzz-dataset (ruff $ruff_pin)"
uvx "ruff@$ruff_pin" check --config pyproject.toml ../buzz-dataset
uvx "ruff@$ruff_pin" format --check --config pyproject.toml ../buzz-dataset

# Stop the benchmark Docker stack (state and channels are kept)
benchmark-down:
docker compose --project-name buzz-benchmark down
24 changes: 23 additions & 1 deletion crates/buzz-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,34 @@ name = "fake-mcp"
path = "tests/bin/fake_mcp.rs"

[dependencies]
# For the `buzz-agent auth <provider>` subcommand only. Provider auth for the
# agent loop itself is goose's; this is the interactive PKCE login that seeds
# the token cache that goose (and the desktop model picker) then read.
buzz-model-catalog = { path = "../buzz-model-catalog" }
goose = { git = "https://github.com/aaif-goose/goose", rev = "fb15d4eade4ab3947b85e20852a18de269c789c9", default-features = false, features = ["rustls-tls"] }
# Consume the GDK loop primitives from their owning crate. Keep this on the
# exact same Goose revision as `goose` so shared conversation types stay unified.
goose-agent = { git = "https://github.com/aaif-goose/goose", rev = "fb15d4eade4ab3947b85e20852a18de269c789c9" }
goose-provider-types = { git = "https://github.com/aaif-goose/goose", rev = "fb15d4eade4ab3947b85e20852a18de269c789c9" }
# ModelConfig: goose re-exports it from goose-providers; goose::model_config
# keeps its own copy private.
goose-providers = { git = "https://github.com/aaif-goose/goose", rev = "fb15d4eade4ab3947b85e20852a18de269c789c9" }
# Resolution guard: goose pins icu_locale "=2.1.1" (needs icu_collections
# ~2.1.1) while url 2.5.x -> idna -> idna_adapter 1.2.2 pulls icu_normalizer
# 2.2.0 (needs icu_collections ~2.2.0). Only one 2.x icu_collections can be
# selected. 1.2.0 is the last idna_adapter on ICU4X 1.x, which keeps IDNA off
# the icu_collections 2.x line entirely and lets both resolve.
idna_adapter = "=1.2.0"
tokio-util = { version = "0.7", features = ["rt"] }
futures = "0.3"
anyhow = "1"

tokio = { workspace = true, features = ["rt-multi-thread", "macros", "io-std", "io-util", "sync", "process", "time", "net"] }
serde = { workspace = true }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
reqwest = { workspace = true, features = ["json", "rustls", "form"] }
rmcp = { version = "1", default-features = false, features = ["client", "transport-child-process"] }
rmcp = { version = "3", default-features = false, features = ["client", "transport-child-process"] }
arc-swap = "1"
getrandom = "0.4"
tracing = { workspace = true }
Expand Down
122 changes: 122 additions & 0 deletions crates/buzz-agent/HANDTEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Hand-testing `buzz-agent`

This is a **swap**, not a new thing. `buzz-agent` keeps its name, its binary,
its ACP contract and its place in the workspace — only the internals changed.
buzz-agent still owns the agent loop (`src/loop_drive.rs`); what changed is
that the loop now calls the `goose` crate for the model stream, the tool
surface, tool dispatch, the system prompt and compaction, instead of using
buzz-agent's own ~8k lines of provider and MCP code.

So there is nothing special to run. The normal flow *is* the test:

```bash
just dev # the whole app
# or
just relay # terminal 1
just goose # terminal 2 — agent against that relay
```

Both already build and use the swapped `buzz-agent`. If you see a difference,
that is the bug.

## Fastest useful check: the stdio smoke script

`just dev` is the real test, but it needs a relay, a desktop app and a human.
To exercise the loop against a **real provider** in ~30 seconds, drive the
binary over its ACP stdio interface directly:

```bash
cargo build -p buzz-agent -p buzz-dev-mcp
export ANTHROPIC_API_KEY=... # or your provider of choice
python3 crates/buzz-agent/scripts/handtest.py # persona + hints
python3 crates/buzz-agent/scripts/handtest.py --tools # real MCP tools
python3 crates/buzz-agent/scripts/handtest.py --stop-veto # _Stop veto
python3 crates/buzz-agent/scripts/handtest.py --cancel # no stuck spinner
python3 crates/buzz-agent/scripts/handtest.py --steer # mid-turn steer
```

Each mode asserts and prints its own verdict. This is the coverage the
automated suite cannot give you: **the suite uses a fake SSE server, so it
never proves a real provider works.**

## What to look at

Ordered by risk. The automated suite (41 tests) covers each of these at the
stdio layer; this list is the part only a human can judge.

### 1. Persona actually arrives

```
@fizz who are you?
```

Should answer *as Fizz* and know the `buzz` CLI exists. A generic goose answer
means the system prompt was dropped — the exact failure that makes plain-ACP
embedding impossible, and the reason this uses the library API instead.

### 2. `_Stop` veto

```
@fizz make a todo list with 3 items, then stop immediately without doing them
```

Must refuse to stop while items are open. Capped at 3 vetoes, so it ends
eventually regardless. Look for `_Stop hook vetoed end of turn` in the log.

### 3. Streaming feel

The old loop emitted one chunk per round; goose streams token-by-token. Tests
prove the relay is not write-amplified (chunks are coalesced by identity key,
flushed at 500ms, paced at 167ms/90-per-minute), but **only a human can say
whether it feels better or worse in the desktop app.** This is the most likely
source of "something is off".

### 4. Cancel mid-tool

Ask for something long (`count slowly to 100 with a shell sleep`), then stop.
The turn should end promptly with **no tool call left spinning**. Cancellation
is a cooperative drain with a 5s budget — dropping the stream instead would
leave the MCP child running and the spinner stuck forever. That bug existed and
is fixed; this is the visual confirmation.

### 5. Steering

Send a second message while the agent is working. It should be absorbed into
the running turn, not cancel-and-restart it.

### 6. Model picker

Should list models, not just the current one. An absent catalog is degraded UX,
never a session failure.

### 7. Tool hygiene — known deviation

The model can now *see* `_Stop` and `_PostCompact`: goose's allowlist gates
advertising and dispatch through the same cache, so hiding them would also make
them undispatchable and break the veto. A system-prompt extension tells the
model to leave them alone. Watch for it calling `_Stop` itself — if that
happens the guidance needs strengthening.

## Comparing against the old behaviour

The old loop is gone, so A/B means checking out `main` in a second worktree and
running that relay side by side.

## Known gaps

* **Anthropic is the only provider hand-tested end-to-end** (via
`scripts/handtest.py`, see above: persona, AGENTS.md hints, 9-model catalog,
real MCP tool call, `_Stop` veto to its cap, cancel-with-no-stuck-spinner,
and a mid-turn steer absorbed without restarting the turn). The automated
suite otherwise uses a fake SSE server. Databricks *chat* now goes through
goose entirely and is unexercised — though Databricks *model discovery*
still uses our own code, moved to `buzz-model-catalog` (the desktop cannot
link goose: native `sqlite3` collision with its own rusqlite).
* **Relay-mesh MoA is restored but only tested against a fake router.** With
`mesh-llm` running, a relay-mesh agent on `auto` should switch to the virtual
`mesh` model within ~2 turns once ≥2 models are live, and fall back to `auto`
within 30s of the mesh shrinking. Grep the agent log for
`relay-mesh auto:`.
* Binary is roughly +22.7 MiB raw / +6.0 MiB gzip.
* Nothing changed in packaging or the harness catalog — same sidecar name, so
nothing needed to.
Loading
Loading