hive-sh4re + docs: extract Approval lifecycle prose (#717 batch 3)

This commit is contained in:
iris 2026-05-31 15:56:04 +02:00 committed by mara
commit a0b15ed6a4
2 changed files with 80 additions and 56 deletions

View file

@ -88,6 +88,55 @@ creates the container; subsequent ones rebuild it with new config.
This gives the manager (and operator) an explicit review gate on the This gives the manager (and operator) an explicit review gate on the
initial configuration before any container is created. initial configuration before any container is created.
### Approval kinds (wire shapes)
`ApprovalKind` carries five variants; each maps to a different
`commit_ref` encoding because that field is overloaded as the
kind-specific payload carrier.
- `ApplyCommit``commit_ref` is the manager-supplied git sha
(7-40 hex chars). The canonical, hive-c0re-vouched sha after the
proposal fetch lives in `fetched_sha` on the same `Approval`
row (only `ApplyCommit` populates it). See the End-to-end flow
above.
- `Spawn` — direct container creation under the default
`agent.nix` template. `commit_ref` is empty. Submitted via
`HostRequest::RequestSpawn` (operator-gated, the
`◆ R3QU3ST SP4WN` dashboard button + `hive-c0re request-spawn`
CLI). The host-level `HostRequest::Spawn` variant bypasses the
approval queue entirely — privileged-context use only (operator
on the host shell, test scripts, one-off recoveries). The
manager-side `RequestSpawn` is gone; managers go through the
`InitConfig``ApplyCommit` two-step instead so the spawn
captures their customised config.
- `InitConfig``commit_ref` is empty; the variant just gates
"seed the proposed repo with the default template" against
operator approval. Step 1 of the two-step spawn flow above.
- `UpdateMetaInputs``commit_ref` stores the JSON-encoded inputs
array (`"[]"` = all inputs, `"[\"nixpkgs\"]"` = just nixpkgs,
etc.). `agent` field is set to `hm1nd` (the requesting manager).
On approve hive-c0re runs `nix flake update [inputs...]` on the
meta flake and commits the resulting lock changes.
- `SchedulePrompt``commit_ref` stores the JSON-encoded
`SchedulePromptPayload` (target list, body, schedule) so the
approval row carries the full submission verbatim. On approve
hive-c0re inserts a row into `scheduled_prompts` with
`source = approval:<id>`; the worker fans the body out as
inbox messages to each target at the scheduled time, recurring
when `interval_seconds` is set.
### Destroy semantics
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down,
not an approval. Stops + removes the nspawn container, drops the
systemd drop-in, fails any pending approvals. Persistent state
(proposed/applied repos, claude credentials, `/state/` notes) is
**kept by default** — recreating the agent with the same name
reuses prior config + login. With `purge = true` the agent's
`/var/lib/hyperhive/{agents,applied}/<name>/` trees are also
wiped (config history + creds + notes gone forever). The manager
refuses to destroy itself.
## Meta flake ## Meta flake
The hive-c0re-owned repo at `/var/lib/hyperhive/meta/` The hive-c0re-owned repo at `/var/lib/hyperhive/meta/`

View file

@ -14,29 +14,17 @@ pub mod assets;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "cmd", rename_all = "snake_case")] #[serde(tag = "cmd", rename_all = "snake_case")]
pub enum HostRequest { pub enum HostRequest {
/// Create and start a sub-agent container directly (no approval). Use /// Create and start a sub-agent container directly, bypassing the
/// this from privileged contexts (operator on the host); it bypasses the /// approval queue. Privileged-context only. See
/// approval queue intentionally so test scripts and one-off recoveries /// `docs/approvals.md::Approval kinds (wire shapes)`.
/// don't need a separate approve step.
Spawn { name: String }, Spawn { name: String },
/// Submit a spawn request for the operator to approve. On approval the /// Submit a spawn request for the operator to approve. See
/// host creates and starts the container with the default `agent.nix` /// `docs/approvals.md::Approval kinds (wire shapes)` (`Spawn`).
/// template. The dashboard's `◆ R3QU3ST SP4WN` button and the
/// `hive-c0re request-spawn` CLI both go through this. The
/// previously-mirrored manager-side `RequestSpawn` was removed
/// (#442) — managers now go through the two-step `request_init_config`
/// and `request_apply_commit` flow so the spawn captures the
/// manager's customised config.
RequestSpawn { name: String }, RequestSpawn { name: String },
/// Stop a managed container (graceful). /// Stop a managed container (graceful).
Kill { name: String }, Kill { name: String },
/// Tear down a sub-agent container: stop + remove + drop the systemd /// Tear down a sub-agent container, optionally purging state.
/// drop-in, purge pending approvals. Persistent state (proposed/applied /// See `docs/approvals.md::Destroy semantics`.
/// repos, Claude credentials) is KEPT by default — recreating the agent
/// with the same name reuses prior config + login. With `purge=true`
/// the agent's `/var/lib/hyperhive/{agents,applied}/<name>/` trees are
/// also wiped (config history + creds + notes gone forever). Manager
/// not destroyable.
Destroy { Destroy {
name: String, name: String,
#[serde(default)] #[serde(default)]
@ -74,22 +62,22 @@ pub struct HostResponse {
pub approvals: Option<Vec<Approval>>, pub approvals: Option<Vec<Approval>>,
} }
/// One row in the approval queue. `commit_ref` is overloaded per
/// `kind` — see `docs/approvals.md::Approval kinds (wire shapes)`
/// for the encoding table and lifecycle.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Approval { pub struct Approval {
pub id: i64, pub id: i64,
pub agent: String, pub agent: String,
#[serde(default)] #[serde(default)]
pub kind: ApprovalKind, pub kind: ApprovalKind,
/// For `ApplyCommit`: the git sha the manager submitted. For `Spawn`: /// Kind-specific payload (git sha / inputs array / schedule
/// empty. Note that this is the manager's *claimed* ref — the /// payload / empty). See the Approval struct doc.
/// canonical, hive-c0re-vouched sha after the proposal fetch lives
/// in `fetched_sha`.
pub commit_ref: String, pub commit_ref: String,
/// The sha hive-c0re fetched from the proposed repo into applied at /// `ApplyCommit` only: the canonical hive-c0re-vouched sha after
/// submission time, then tagged `proposal/<id>`. Stable for the /// the proposal fetch, tagged `proposal/<id>`. Stable for the
/// lifetime of the approval — manager amends in proposed don't /// approval's lifetime — manager amends in proposed don't change
/// change what gets built. Only set for `ApplyCommit` after the /// what gets built.
/// successful fetch.
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub fetched_sha: Option<String>, pub fetched_sha: Option<String>,
pub requested_at: i64, pub requested_at: i64,
@ -98,44 +86,31 @@ pub struct Approval {
pub resolved_at: Option<i64>, pub resolved_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>, pub note: Option<String>,
/// Optional free-text description the manager attached at submission /// Free-text description the manager attached at submission time;
/// time — shown on the dashboard approval card so the operator can /// shown on the dashboard approval card.
/// understand the change without opening the diff.
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>, pub description: Option<String>,
} }
/// What action the approval, when granted, will trigger. /// What action the approval, when granted, will trigger.
/// Variant-specific payload encoding + flow lives in
/// `docs/approvals.md::Approval kinds (wire shapes)`.
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ApprovalKind { pub enum ApprovalKind {
/// Apply a manager-proposed config commit (existing flow). /// Apply a manager-proposed config commit.
#[default] #[default]
ApplyCommit, ApplyCommit,
/// Create + start a new sub-agent container with the given name. /// Create + start a new sub-agent container with the given name
/// Used today by the host admin socket / dashboard "request spawn" /// (under the default `agent.nix` template).
/// button; the manager-side `RequestSpawn` surface was removed in
/// favour of the two-step `InitConfig` → `ApplyCommit` flow (#442).
Spawn, Spawn,
/// Initialise a new agent's proposed config repo so the manager can /// Seed an agent's proposed config repo for manager customisation
/// customise it before submitting a `request_apply_commit`. On /// (step 1 of the two-step spawn flow).
/// approval hive-c0re seeds `proposed/<name>/` with the default
/// `agent.nix` template but does NOT create the container — that
/// requires a subsequent `ApplyCommit` approval pinning the
/// customised config sha.
InitConfig, InitConfig,
/// Run `nix flake update [inputs...]` on the meta flake and commit /// Run `nix flake update [inputs...]` on the meta flake and commit
/// the resulting lock changes. The `commit_ref` field stores the /// the resulting lock changes.
/// JSON-encoded inputs array (`"[]"` = all inputs). Agent field is
/// set to `hm1nd` (the requesting manager).
UpdateMetaInputs, UpdateMetaInputs,
/// Add a scheduled prompt (closes #444). On approval hive-c0re /// Add a scheduled prompt to the broker queue.
/// inserts a row into `scheduled_prompts` with
/// `source = Approval { id }`; the worker fans the body out as
/// inbox messages to each target at the scheduled time, recurring
/// when `interval_seconds` is set. `commit_ref` stores the
/// JSON-encoded `SchedulePromptPayload` so the approval row carries
/// the full submission verbatim (target list, body, schedule).
SchedulePrompt, SchedulePrompt,
} }
@ -146,10 +121,10 @@ pub enum ApprovalStatus {
Approved, Approved,
Denied, Denied,
Failed, Failed,
/// Manager withdrew the request before the operator acted on it /// Manager withdrew the request before the operator acted on it.
/// (closes #250). Distinct from `Denied` (operator decision) and /// Distinct from `Denied` (operator decision) and `Failed`
/// `Failed` (post-approval lifecycle error) so the dashboard can /// (post-approval lifecycle error). See
/// chip / sort cancellations separately. /// `docs/approvals.md::Withdrawing a pending approval`.
Cancelled, Cancelled,
} }