Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
60dacc5
feat(desktop): list private known Desktops
loganj Sep 4, 2026
ff5b04f
fix(relay): admit delayed immutable Desktop profiles
loganj Sep 4, 2026
271fefd
test(desktop): match unsigned relay ingest drift constant
loganj Sep 4, 2026
e5812f0
feat(desktop): show private per-Desktop last-heard observations
loganj Sep 4, 2026
23e8f85
fix(desktop): fence delayed pulses and order host observations
loganj Sep 4, 2026
0fdf436
feat(desktop): report private per-Desktop runtime capabilities
loganj Sep 4, 2026
09282c7
fix(desktop): defer capability changes until real time advances
loganj Sep 4, 2026
d67471c
feat(multiverse): add private immutable Desktop Stop transport
loganj Sep 4, 2026
a5b3674
feat(desktop): fence automatic launches after remote Stop
loganj Sep 4, 2026
18a0d17
feat(desktop): authenticate and durably consume remote Stop
loganj Sep 4, 2026
17e08d7
feat(desktop): add scope-bound Stop client and exact-request retry
loganj Sep 4, 2026
20436c8
feat(desktop): mount private remote Stop controls and live receiver
loganj Sep 4, 2026
c6be538
test(multiverse): exercise mounted Stop recovery and private fanout i…
loganj Sep 4, 2026
83a2df1
feat(multiverse): add private immutable lifecycle transport
loganj Sep 4, 2026
b85c532
feat(desktop): persist ordered placement and lifecycle admission
loganj Sep 4, 2026
0f2eb99
feat(desktop): introduce destination-local keyless launch boundary
loganj Sep 4, 2026
faee0ca
fix(multiverse): defer provisioning stub until receiver slice
loganj Sep 4, 2026
a52c053
feat(desktop): receive authenticated Start and current-host Restart
loganj Sep 4, 2026
30ce5b9
fix(multiverse): introduce provisioning stub with its receiver
loganj Sep 4, 2026
8b26bb2
feat(desktop): coordinate failure-final Move and exact lifecycle retry
loganj Sep 4, 2026
def4f23
feat(desktop): mount Start Restart and Move controls
loganj Sep 4, 2026
c6f3e3e
fix(multiverse): retain superseded Stop outcomes for exact retry
loganj Sep 4, 2026
7d00439
test(multiverse): scope Stop row and exercise mounted launch refusal
loganj Sep 4, 2026
943b62a
fix(desktop): keep lifecycle receiver failures out of shell layout
loganj Sep 4, 2026
cbb4091
fix(desktop): preserve launch community separately from runtime aliases
loganj Sep 5, 2026
a7d32b9
fix(desktop): diagnose and explicitly recover lifecycle receivers
loganj Sep 5, 2026
18258f9
fix(desktop): observe receiver closure after subscription readiness
loganj Sep 5, 2026
6bea46f
fix(desktop): bound lifecycle receiver recovery without command replay
loganj Sep 5, 2026
ef58314
fix(desktop): do not retry terminal receiver initialization
loganj Sep 5, 2026
546f5ec
fix(desktop): preserve runtime community authority and refuse lossy r…
loganj Sep 5, 2026
4ae32aa
feat(desktop): checkpoint scoped runtime configuration launches
loganj Sep 7, 2026
7da7c39
fix(desktop): bind prepared launch inputs and enforce selected ACP model
loganj Sep 7, 2026
eb109b8
fix(desktop): require captured preflight authority for runtime launches
loganj Sep 7, 2026
ee6395b
refactor(desktop): satisfy runtime integration file-size ratchet
loganj Sep 7, 2026
651cb1b
fix(desktop): retire lifecycle continuations when controls are canceled
loganj Sep 7, 2026
3357948
feat(desktop): bind lifecycle switching to exact runtime configurations
loganj Sep 7, 2026
feb900a
test(core): exercise malformed lifecycle observations through encrypt…
loganj Sep 7, 2026
a35d949
test(desktop): fence expired readiness after confirmed source Stop
loganj Sep 7, 2026
a7d1357
fix(desktop): integrate lifecycle with captured launch admission
loganj Sep 7, 2026
6cb9d2f
fix(desktop): repair native configuration integration compilation
loganj Sep 7, 2026
12f7570
test(desktop): preserve host binding before moving configuration fixture
loganj Sep 7, 2026
b8d72f5
fix(desktop): use destination-local credentials for remote launch
loganj Sep 7, 2026
007035a
test(ci): execute remote credential fixtures on integrated native can…
loganj Sep 7, 2026
1c4428c
fix(desktop): restore captured session policy command writer
loganj Sep 7, 2026
61943e1
fix(desktop): clear native clippy gate and fresh retention scope init
loganj Sep 7, 2026
ef10f18
fix(desktop): make lifecycle persistence credential neutral
loganj Sep 7, 2026
e0b2a37
test(desktop): prove revocation at real prior-session teardown
loganj Sep 8, 2026
98f9635
test(desktop): exercise lifecycle refusal through configuration catalog
loganj Sep 8, 2026
600b762
test(db): trace one safe tuple from refused activity aggregate
loganj Sep 8, 2026
639a450
test(desktop): exercise explicit switching between two named configur…
loganj Sep 8, 2026
8b4f9d6
test(desktop): disambiguate workflow step text fills and settle inspe…
loganj Sep 8, 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
4 changes: 4 additions & 0 deletions .github/workflows/_ci-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ jobs:
run: just desktop-tauri-check
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
- name: Desktop Tauri remote credential fixtures
run: cargo test --manifest-path desktop/src-tauri/Cargo.toml --no-default-features managed_agents::runtime_configurations::tests::remote_credentials -- --test-threads=1
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
- name: Desktop Tauri tests
run: just desktop-tauri-test
env:
Expand Down
4 changes: 3 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,10 @@ test-unit:
# non-postgres_tests cases only "pass" without a database by waiting out
# the ~30s sqlx acquire timeout, so they do not belong in the infra-free
# unit job either.
# The author-only fanout family uses lazy pools and in-memory recipients;
# include Stop request/result privacy and its sibling private kinds.
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)) + test(/^handlers::channel_authz::/) + test(/^handlers::moderation_authz::/) + test(/^handlers::side_effects::tests::/)'
-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)) + test(/^handlers::channel_authz::/) + test(/^handlers::moderation_authz::/) + test(/^handlers::side_effects::tests::/) + test(/^handlers::event::tests::fanout_access::.*_delivers_to_author_only$/)'
# ACP author-gate and queue tests protect the trust boundary between
# relay events and agent prompts. They are infra-free; ignored lifecycle
# tests remain excluded and run in their dedicated integration lanes.
Expand Down
56 changes: 54 additions & 2 deletions crates/buzz-acp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2496,6 +2496,10 @@ async fn tokio_main() -> Result<()> {
.init();

let mut config = Config::from_cli().map_err(|e| anyhow::anyhow!("configuration error: {e}"))?;
let (startup_model, require_model) = startup_model_selection(
config.model.as_deref(),
std::env::var("BUZZ_ACP_REQUIRED_MODEL"),
)?;

// ── Setup-mode early branch ───────────────────────────────────────────────
//
Expand Down Expand Up @@ -3100,7 +3104,8 @@ async fn tokio_main() -> Result<()> {
acp,
state: SessionState::default(),
model_capabilities: None,
desired_model: config.model.clone(),
desired_model: startup_model.clone(),
require_model,
model_overridden: false,
desired_model_request_id: None,
desired_model_pending_ack: false,
Expand Down Expand Up @@ -5442,10 +5447,29 @@ impl PoolStartup {
}
}

// Named launches carry the target, not a boolean claiming it was selected.
// This is independent of BUZZ_ACP_MODEL: Claude A1 deliberately removes that
// switch hint and selects through ANTHROPIC_MODEL at launch. Session creation
// must still verify the required target from the adapter's actual response.
fn startup_model_selection(
legacy_model: Option<&str>,
required_model: Result<String, std::env::VarError>,
) -> Result<(Option<String>, bool)> {
match required_model {
Ok(model) if !model.trim().is_empty() => Ok((Some(model), true)),
Err(std::env::VarError::NotPresent) => Ok((legacy_model.map(str::to_owned), false)),
_ => anyhow::bail!("BUZZ_ACP_REQUIRED_MODEL must be a non-empty UTF-8 model ID"),
}
}

async fn initialize_agent_pool(
startup: &PoolStartup,
mut shutdown: Option<watch::Receiver<()>>,
) -> Result<AgentPool> {
let (desired_model, require_model) = startup_model_selection(
startup.model.as_deref(),
std::env::var("BUZZ_ACP_REQUIRED_MODEL"),
)?;
// One agent failing to start must not kill the whole pool.
// Attempt each spawn under a 60-second timeout; a partial pool is valid.
let mut agent_slots: Vec<Option<OwnedAgent>> = Vec::with_capacity(startup.agents as usize);
Expand Down Expand Up @@ -5502,7 +5526,8 @@ async fn initialize_agent_pool(
acp,
state: SessionState::default(),
model_capabilities: None,
desired_model: startup.model.clone(),
desired_model: desired_model.clone(),
require_model,
model_overridden: false,
desired_model_request_id: None,
desired_model_pending_ack: false,
Expand Down Expand Up @@ -9358,6 +9383,7 @@ mod error_outcome_emission_tests {
state: Default::default(),
model_capabilities: None,
desired_model: None,
require_model: false,
model_overridden: false,
desired_model_request_id: None,
desired_model_pending_ack: false,
Expand Down Expand Up @@ -11248,3 +11274,29 @@ mod observer_payload_trim_tests {
assert!(leaf.contains("[elided"));
}
}

#[cfg(test)]
mod startup_model_selection_tests {
use super::startup_model_selection;
use std::env::VarError;

#[test]
fn required_target_is_independent_of_legacy_switch_hint() {
for legacy in [None, Some("legacy")] {
assert_eq!(
startup_model_selection(legacy, Ok("chosen".into())).unwrap(),
(Some("chosen".into()), true),
);
assert_eq!(
startup_model_selection(legacy, Err(VarError::NotPresent)).unwrap(),
(legacy.map(str::to_owned), false),
);
}
for value in ["", " "] {
assert!(startup_model_selection(Some("legacy"), Ok(value.into())).is_err());
}
assert!(
startup_model_selection(None, Err(VarError::NotUnicode("invalid".into()))).is_err()
);
}
}
Loading
Loading