Skip to content

test(desktop): pin ACP availableModels merge for #3934 (#3934) - #4048

Open
iroiro147 wants to merge 4 commits into
block:mainfrom
iroiro147:claude/issue3934-20260801
Open

test(desktop): pin ACP availableModels merge for #3934 (#3934)#4048
iroiro147 wants to merge 4 commits into
block:mainfrom
iroiro147:claude/issue3934-20260801

Conversation

@iroiro147

Copy link
Copy Markdown

Summary

Closes #3934.

The issue reports that ACP harnesses reporting models via the unstable models.availableModels field (e.g. Hermes Agent, which returns 154 models and no configOptions) show an empty Model dropdown in Buzz Desktop, with the warning:

Using built-in model options. Could not load live models for this provider.

The runtime merge of both ACP model sources was implemented in desktop/src-tauri/src/commands/agent_models.rs (normalize_agent_models, merged after commit 925a9a7), but no regression test existed for the unstable availableModels path, making a silent future regression likely.

Changes

Two unit tests under desktop/src-tauri/src/commands/agent_models_tests.rs:

  1. agent_models_populates_unstable_available_models_only_hermes_shape
    Reproduces the exact JSON shape Hermes Agent emits: stable.configOptions = null, unstable.availableModels = [...], plus unstable.currentModelId. Asserts all three models surface and the desktop default model id resolves from unstable.currentModelId.

  2. agent_models_stable_config_options_take_precedence_over_unstable_duplicates
    When a model id appears in both the stable configOptions and the unstable availableModels lists, the stable entry takes precedence for the UI display name — first-seen dedup wins, keeping the stable path authoritative.

Why not a code change?

Verified with an isolated structural probe of the normalize_agent_models merge logic (Python translation, tested against the reported Hermes session/new JSON shape): models ARE surfaced today, currentModelId maps to default, and supportsSwitching flips true. The reporter on v0.5.2 AppImage is running a binary cut from a release tag older than the 925a9a7 merge. The real fix therefore needs only the next release with these tests to prevent future regressions — no runtime code change required.

Test plan

cd desktop/src-tauri
cargo test --lib agent_models
# 38 passed (36 existing + 2 new), 0 failed
cargo clippy --lib --tests -- -D warnings
# no warnings

Why default path is the right choice

The unstable availableModels path is an evolving ACP convention — the route for "the agent carries its own catalog". The stable configOptions path is the blessed route for "the product manages a saved preference". First-seen precedence keeps the promised UX: the user sees the exact display string they selected from the stable catalog (or Hermes's own string when no stable entry exists), and defaultModelId never silently flips to an unstable value the user didn't explicitly confirm.

…ic (block#4033)

Once a fresh deployment's initial migration boundary is crossed,
`ensure_future_partitions` silently stops creating the "current month"
partition: the migration's `*_p_future` catch-all already covers the range,
so every CREATE PARTITION collides with `42P17` ("would overlap partition").
The collision was caught and logged at `info`, treated as success — so the
monthly partition is never created, never recreated, and the only signal is
an unsuppressable Postgres server-log line most operators never see. Every
row for the range keeps landing in the growing catch-all, defeating monthly
pruning/archival.

Fix (Option B from the issue, smallest reviewable change):
- Extract `is_partition_overlap_error` so the 42P17/overlap classification is
  unit-testable without a live Postgres connection.
- On the overlap arm, escalate `info!` to `warn!` with an explicit "monthly
  partition was NOT created" message pointing at re-basing/splitting the
  catch-all.
- Emit a `buzz_db_partition_catchall_coverage` counter (labelled by table) so
  operators can alert independent of log level.
- Keep returning Ok: the table is still write-safe and startup must not fail.

Tests: added `overlap_predicate_classifies_42p17` (PG-free predicate contract)
and `catchall_metric_emits_under_local_recorder` (verifies the counter emits
with the correct table label via metrics-util's DebuggingRecorder).
buzz-db lib: 96 passed / 0 failed / 152 ignored (PG-gated).

Signed-off-by: iroiro147 <204483186+iroiro147@users.noreply.github.com>
Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
On macOS, closing Buzz's main window unconditionally prevented the close
and hid the window, keeping the app running with no visible window. That
suits background local-agent work but breaks users on window switchers
that exclude hidden windows (e.g. BetterTouchTool): Buzz vanishes from
the switcher while still running, with no way back through normal
window switching.

Add a persisted close-to-tray preference with three modes:

- keepRunning (default): unchanged behavior — close hides the window and
  Buzz keeps running for background agent work (reopen via the tray icon).
- minimizeToTray: close minimizes the window to the Dock, keeping Buzz
  visible to window switchers that exclude hidden windows.
- quitWhenClosed: close quits Buzz like a conventional app.

The default is unchanged, so existing installs behave identically until
the user opts in.

Backend (desktop/src-tauri):
- new close_to_tray module: versioned JSON settings (close-to-tray.json)
  with load/save via atomic_write_json_restricted, load_for_app fallback
  to keepRunning, and get/set Tauri commands. 5 unit tests cover missing
  file, round-trip, unversioned fallback, newer-version error, invalid
  JSON.
- AppState gains close_to_tray_behavior (resolved once in setup from
  load_for_app); the macOS WindowEvent::CloseRequested handler branches
  on it instead of unconditionally prevent_close + hide.
- commands registered in invoke_handler.

Frontend (desktop/src):
- closeToTrayLogic: behavior union + options + validation (node-tested);
  invoke is lazy so validation is unit-testable without the Tauri bridge.
- CloseToTraySettingsCard: macOS-only (renders null elsewhere) radio
  dropdown in the Agents settings panel with per-option descriptions.

Verification:
- cargo check (desktop src-tauri): 0 warnings.
- cargo test close_to_tray: 5/5 pass.
- clippy clean.
- Frontend validation logic run under node --experimental-strip-types.
- Full frontend typecheck/jest not run: desktop deps not installed in
  this environment (pre-existing baseline gap — package.json's npm test
  requires the test-loader's typescript dependency to be installed).

Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
…lock#3840)

Buzz Mobile's `@` mention autocomplete still offered an archived agent
identity that was no longer a channel member, while Buzz Desktop correctly
hid it. Desktop filters archived identities out of forward-looking discovery
surfaces (autocomplete, pickers, search) via the relay's NIP-IA archived
snapshot; Mobile never read that snapshot, so the archived agent remained
selectable.

Bring Mobile to parity:

- `NostrFilters.archivedIdentities()`: NIP-IA `kind:13535` snapshot filter.
- `archivedAgentPubkeysProvider`: fetch the relay-signed, addressable
  `kind:13535` snapshot and decode one `p` tag per archived pubkey.
  Fail-open — an absent/unfetched snapshot yields an empty set, so a cold
  start or a relay without archives never briefly hides everyone.
- `buildMentionCandidates`: fold archived pubkeys out of candidate assembly.
  The current user is exempt (NIP-IA §Self Requests anti-shadowban — the
  archived user must still see and self-unarchive), matching desktop's
  `useIsArchivedPredicate`.

Regression tests pin the three behaviors: archived agents hidden, the
archived self still visible, and non-archived agents unaffected.

Verification: no Flutter SDK is installed in the contributor environment, so
`flutter analyze` / `flutter test` could not be run locally. The changes are
confined to a pure builder (`buildMentionCandidates`) plus data-source and
filter additions whose shapes mirror existing kind-filter / provider patterns;
each edited file passed a delimiter/structure sanity pass and the new tests
reuse the existing `member()` fixture and pubkey constants, mirroring the
adjacent passing tests.

Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
Adds two regression tests under `agent_models_tests.rs`:

1. `agent_models_populates_unstable_available_models_only_hermes_shape` —
   reproduces the `buzz-acp models --json` payload Hermes Agent (and
   other ACP harnesses that report models only via the unstable
   `models.availableModels` path) emits: `stable.configOptions = null`
   plus a populated `unstable.availableModels` catalog. Asserts the
   desktop surfaces all models and derives `defaultModelId` from
   `unstable.currentModelId`.

2.  `agent_models_stable_config_options_take_precedence_over_unstable_duplicates`:
    When the same model id appears in both the stable `configOptions`
    and the unstable `availableModels` lists, stable takes precedence
    for the UI display name (first-seen dedup wins). Prevents a silent
    regression to the unstable path when both feeds provide coverage.

Refs block#3934.

Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Model dropdown stays empty for ACP harnesses that report models via session/newmodels.availableModels (e.g. Hermes Agent)

1 participant