kick_agent on every rebuild + apply path

agents weren't being woken with the 'you were rebuilt — check
/state/ for notes, --continue intact' system message after
several recent rebuild surfaces:

- auto_update::rebuild_agent — used by the dashboard rebuild
  button, admin-CLI rebuild via lifecycle_action, the startup
  rev-scan, AND the new meta-input update batch loop. kick
  moves *into* rebuild_agent's success arm so all four
  paths benefit. (the dashboard's lifecycle_action extra
  closure was already firing kick — now it's a no-op for the
  rebuild path since rebuild_agent does it.)
- actions::run_apply_commit — apply-commit approve flow built
  + tagged deployed/<id> but never kicked. add kick on
  success with the more specific 'config update applied' hint.
- server.rs::HostRequest::Rebuild — the admin-CLI direct path
  calls lifecycle::rebuild bypassing rebuild_agent. add kick
  on success.

dashboard's restart / start lifecycle_action extras still
kick via their own closures since they don't route through
rebuild_agent. stop / kill / destroy intentionally don't
kick — there's nothing to wake.
This commit is contained in:
müde 2026-05-16 04:20:01 +02:00
parent 78aa830430
commit d06b598c56
4 changed files with 30 additions and 11 deletions

View file

@ -239,9 +239,12 @@ async fn run_apply_commit(
// operator can git-commit by hand if they care.
tracing::warn!(agent = %approval.agent, %id, error = ?e, "meta finalize_deploy failed");
}
// Don't ignore the coord pointer — keeps the borrow alive
// for future tracing additions without re-plumbing.
let _ = coord;
// Wake the agent on its next turn so claude sees the
// config change took effect. Same hint pattern as
// auto_update::rebuild_agent — manager approved a
// proposal, agent picks up where it left off with the
// new env / packages.
coord.kick_agent(&approval.agent, "config update applied");
(Ok(()), Some(tag))
}
Err(e) => {

View file

@ -88,6 +88,13 @@ pub async fn rebuild_agent(coord: &Arc<Coordinator>, name: &str, current_rev: &s
sha: None,
tag: None,
});
// Wake the agent on its next turn so claude sees a
// "you were rebuilt — check /state/ for notes, --continue
// session intact" hint. Covers dashboard rebuild, admin
// CLI rebuild, auto-update startup scan, and the
// dashboard's meta-input update path — all of which
// route through rebuild_agent.
coord.kick_agent(name, "container rebuilt");
}
Err(e) => {
coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {

View file

@ -1093,7 +1093,9 @@ async fn post_rebuild(State(state): State<AppState>, AxumPath(name): AxumPath<St
let rev = current_rev.clone();
async move { crate::auto_update::rebuild_agent(&coord, &n, &rev).await }
},
|s, n| s.coord.kick_agent(n, "container rebuilt"),
// rebuild_agent fires kick_agent on success itself, so the
// extra-closure is a no-op here.
|_, _| {},
)
.await
}

View file

@ -147,13 +147,20 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
// agent.nix). Without this the admin-socket CLI was
// a notify-gap.
match &result {
Ok(()) => coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
Ok(()) => {
coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
agent: name.clone(),
ok: true,
note: None,
sha: None,
tag: None,
}),
});
// Wake the agent's next turn with the
// "you were rebuilt" hint. Same pattern as
// auto_update::rebuild_agent and the dashboard
// rebuild path — this is the CLI's equivalent.
coord.kick_agent(name, "container rebuilt");
}
Err(e) => coord.notify_manager(&hive_sh4re::HelperEvent::Rebuilt {
agent: name.clone(),
ok: false,