From f3fefe7f7e1e1b8aa55748291a783e6202edd696 Mon Sep 17 00:00:00 2001 From: iris Date: Sun, 31 May 2026 16:16:24 +0200 Subject: [PATCH] hive-sh4re + docs: extract HelperEvent prose (#717 batch 7) --- docs/approvals.md | 11 ++++++ hive-sh4re/src/lib.rs | 79 ++++++++++--------------------------------- 2 files changed, 29 insertions(+), 61 deletions(-) diff --git a/docs/approvals.md b/docs/approvals.md index ff1ab9c7..c9d988d2 100644 --- a/docs/approvals.md +++ b/docs/approvals.md @@ -497,6 +497,17 @@ regular claude turn so the manager can react. Variants The recipient responds via `Answer { id, answer }` and the asker sees the matching `QuestionAnswered`. +Optional `sha` / `tag` fields on `ApprovalResolved`, `Spawned`, and +`Rebuilt` carry the canonical hive-c0re-vouched commit sha plus the +applied-repo's terminal tag (`deployed/` / `failed/` / +`denied/` for approval-driven flows; `approved/` for the +rare bare-approval case where no underlying action runs). Both are +`Option`: `None` on the paths that don't change the deployed commit +(e.g. `auto_update::rebuild_agent` reapplying the existing main, or +the dashboard `↻ R3BU1LD` button when the lock didn't move). When +set, `git show ` against `/agents//applied.git` inside the +manager container yields the exact tree that was referenced. + To add a new event: new `HelperEvent` variant + call sites + update `prompts/system.md` (`` block, the lifecycle- event list) so the manager knows the new shape. diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index 2c5d95d0..2fd1c544 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -519,11 +519,12 @@ pub const SYSTEM_SENDER: &str = "system"; /// Out-of-band events the host-side daemon pushes to the manager's inbox. /// Serialised as JSON in `Message::body` (sender = `SYSTEM_SENDER`). +/// Per-variant triggers + the optional `sha`/`tag` semantics live in +/// `docs/approvals.md::Helper events to the manager`. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "event", rename_all = "snake_case")] pub enum HelperEvent { - /// An approval was approved/denied/failed; if approved, the underlying - /// action (rebuild or spawn) has already run by the time this lands. + /// An approval transitioned to a terminal state. ApprovalResolved { id: i64, agent: String, @@ -531,105 +532,61 @@ pub enum HelperEvent { status: ApprovalStatus, #[serde(default, skip_serializing_if = "Option::is_none")] note: Option, - /// Canonical sha hive-c0re fetched into applied at submission - /// time. `git show ` against `/agents//applied.git` - /// inside the manager container yields the exact tree being - /// referenced. #[serde(default, skip_serializing_if = "Option::is_none")] sha: Option, - /// Terminal tag name in the applied repo for this approval — - /// `deployed/`, `failed/`, or `denied/` (and - /// `approved/` for the rare bare-approval case where - /// no underlying action runs). #[serde(default, skip_serializing_if = "Option::is_none")] tag: Option, }, - /// A new container was spawned (post-approval or via the admin CLI - /// bypass path). `ok=false` means the spawn failed. + /// A new container was spawned. `ok = false` = spawn failed. Spawned { agent: String, ok: bool, #[serde(default, skip_serializing_if = "Option::is_none")] note: Option, - /// Sha of the `deployed/0` commit seeded by hive-c0re on - /// first spawn (Some on success, None on failure). #[serde(default, skip_serializing_if = "Option::is_none")] sha: Option, }, - /// A container was rebuilt (auto-update on flake rev change, or a - /// manual rebuild from CLI/dashboard). + /// A container was rebuilt (auto-update or manual). Rebuilt { agent: String, ok: bool, #[serde(default, skip_serializing_if = "Option::is_none")] note: Option, - /// Sha that ended up at `deployed/` on success, or the - /// proposal sha that just got tagged `failed/` on - /// failure. None for the (rare) rebuild path that doesn't go - /// through an approval (e.g. `auto_update::rebuild_agent` - /// reapplying the existing main). #[serde(default, skip_serializing_if = "Option::is_none")] sha: Option, - /// `deployed/` or `failed/` for approval-driven - /// rebuilds; None for auto-update / dashboard rebuilds that - /// don't change the deployed commit. #[serde(default, skip_serializing_if = "Option::is_none")] tag: Option, }, - /// A new agent's proposed config repo was initialised (post-`InitConfig` - /// approval). The manager can now edit `/agents//config/agent.nix`, - /// commit the changes, and submit a `RequestApplyCommit` — which will - /// create the container on first deploy, then rebuild on every subsequent - /// deploy. + /// A new agent's proposed config repo was seeded post-`InitConfig`. ConfigReady { agent: String }, - /// A sub-agent's container was stopped (the systemd unit is down; - /// persistent state is unchanged). + /// A sub-agent's container was stopped (systemd unit down; state kept). Killed { agent: String }, - /// A sub-agent's container was torn down (container removed; state - /// dirs preserved per `destroy` semantics). + /// A sub-agent's container was torn down (state dirs preserved by default). Destroyed { agent: String }, - /// A sub-agent's container has no claude session yet (first - /// spawn, or `--purge` wiped creds). Manager can't do anything - /// about it directly — login is interactive OAuth — but it - /// surfaces so the manager knows the agent is in partial-run - /// mode and can flag the operator. + /// A sub-agent's container has no claude session yet. NeedsLogin { agent: String }, - /// An agent successfully completed claude login — the session - /// dir now contains creds. Transition fires once per login. + /// A sub-agent just completed claude login. LoggedIn { agent: String }, - /// An agent's recorded flake rev is stale relative to the - /// current hyperhive rev. The manager has the `update` tool to - /// trigger a rebuild without operator approval (it's a no-op - /// when nothing actually changed). + /// A sub-agent's recorded flake rev is stale relative to hyperhive. NeedsUpdate { agent: String }, - /// Container exited without an operator-initiated stop. Fired by - /// the crash watcher when an agent's container transitions from - /// running → stopped and no `Stopping` / `Restarting` / - /// `Destroying` transient was set, so the operator (or the - /// manager) knows it crashed rather than was killed on purpose. + /// Container exited without an operator-initiated stop (crash). ContainerCrash { agent: String, #[serde(default, skip_serializing_if = "Option::is_none")] note: Option, }, - /// A question queued via `Ask` was answered (by the operator via - /// the dashboard, or by another agent via `Answer`). `id` matches - /// the `QuestionQueued.id` returned to the asker; `question` - /// echoes the original prompt so the asker can stitch the answer - /// back to context across compactions; `answerer` is who answered - /// (`"operator"` or a peer agent name). + /// A question queued via `Ask` was answered. `id` matches the + /// originating `QuestionQueued.id`; `answerer` is `"operator"` / + /// a peer agent name / `"ttl-watchdog"` on expiry. QuestionAnswered { id: i64, question: String, answer: String, answerer: String, }, - /// A peer (or the manager) asked this agent a question via - /// `Ask { to: Some(), ... }`. The recipient should - /// answer via `Answer { id, answer }` on their socket; the answer - /// will route back to the asker as a `QuestionAnswered` event. - /// `options` + `multi` mirror the original `Ask` args so the - /// answerer knows what shape of reply is expected. + /// A peer (or the manager) asked this agent a question. Recipient + /// replies via `Answer { id, answer }`; the answer routes back to + /// the asker as `QuestionAnswered`. QuestionAsked { id: i64, asker: String,