docs: fix genuine passive-voice hits in docs/agent-lifecycle
Ninth batch of the ongoing write-good.Passive pass (hyperhive#4042):
read all 75 hits across agent-hierarchy.md/persistence.md/approvals.md
in context and rewrote 46 with a clearly nameable actor -- mostly
hive-c0re, a specific fn/type named right there or a sentence or two
earlier (approvals.md's numbered "Reminder delivery" list all share
hive-c0re as the established actor across three consecutive bullets),
or "the worker"/"the daemon" reused from the section's own established
subject.
Left 29 alone. Recurring buckets: predicate-adjective state pairs
("is wedged"/"is stopped", "is unaffected", "are unrelated", "is
overloaded" -- matches the established "is trusted"/"is privileged"
family), negative-capability/invariant idioms ("no X is needed",
"can't be removed/started", "no such transient was cleared"),
forward-looking design-intent statements about not-yet-built
enforcement (agent-hierarchy.md's whole "Planned topology semantics"
section leans on this: "are meant to run", "once enforcement is
finished", "will be gated"), the "audit trail, not cache" policy
invariant restated twice verbatim ("Approvals are kept forever" /
"indefinitely" -- left both alone consistently), and a config-
conditional state idiom ("when interval_seconds is set"). One more
false-positive tokenization, same bug as knowledge.md's earlier case:
persistence.md's "`config` is read-only" trips vale's "is read" match
inside the compound adjective -- not a real passive at all.
One deliberately-conservative leave: agent-hierarchy.md's "Reminder
cancellation is handled fully in-agent" had an ambiguous actor (the
in-agent socket vs. the hive-c0re source file the doc points readers
to for detail) -- left alone rather than risk a wrong attribution,
unlike the higher-confidence rewrites elsewhere in the same batch.
Verified via vale before/after: 75 -> 29 write-good.Passive hits,
exactly the 29 left alone above; error count and other warning
categories unchanged. Re-read every changed line in full surrounding
context after editing before running the final vale check.
This commit is contained in:
parent
09196a44fe
commit
0ad0376fe3
3 changed files with 67 additions and 68 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
# Agent hierarchy & privileges
|
# Agent hierarchy & privileges
|
||||||
|
|
||||||
Every agent has a place in an operator-editable parent/child tree, used
|
Every agent has a place in an operator-editable parent/child tree, used
|
||||||
to scope which agents can manage which others. This doc covers how the
|
to scope which agents can manage which others. This doc covers how
|
||||||
tree is stored and edited today, the rules that are meant to run on top
|
hive-c0re stores and edits the tree today, the rules that are meant to run on top
|
||||||
of it once enforcement is finished, and where the manager still gets
|
of it once enforcement is finished, and where the manager still gets
|
||||||
special-cased in the meantime. Tracking issue: hyperhive#361
|
special-cased in the meantime. Tracking issue: hyperhive#361
|
||||||
(`$HIVE_FORGE_URL/hyperhive/hyperhive/issues/361`).
|
(`$HIVE_FORGE_URL/hyperhive/hyperhive/issues/361`).
|
||||||
|
|
@ -24,7 +24,7 @@ Topology lives in the hive-c0re-owned **meta repo**, alongside
|
||||||
structural manager that everything hangs under. Hierarchy is built
|
structural manager that everything hangs under. Hierarchy is built
|
||||||
explicitly: an agent that requests a sub-agent gets a
|
explicitly: an agent that requests a sub-agent gets a
|
||||||
requester-as-parent edge written at its `init_config` approval (so
|
requester-as-parent edge written at its `init_config` approval (so
|
||||||
`bob` above was spawned by `alice`), and the operator can reparent any
|
`alice` spawned `bob` above), and the operator can reparent any
|
||||||
agent, including the bootstrap container (`ruth`) — it's just another
|
agent, including the bootstrap container (`ruth`) — it's just another
|
||||||
root. The manager is reparentable like any other agent; there's no
|
root. The manager is reparentable like any other agent; there's no
|
||||||
"structurally root" carve-out. Its privileges live on its MCP socket,
|
"structurally root" carve-out. Its privileges live on its MCP socket,
|
||||||
|
|
@ -64,11 +64,11 @@ where system-level facts live.
|
||||||
install that hasn't synced yet).
|
install that hasn't synced yet).
|
||||||
- **Reconcile** — runs alongside the periodic meta/flake regeneration.
|
- **Reconcile** — runs alongside the periodic meta/flake regeneration.
|
||||||
New agents default to root unless they already carry an explicit
|
New agents default to root unless they already carry an explicit
|
||||||
parent edge from an `init_config` approval; existing entries
|
parent edge from an `init_config` approval; Reconcile preserves existing entries
|
||||||
(including operator overrides) are preserved; removed agents drop.
|
(including operator overrides); removed agents drop.
|
||||||
Agents that are approved but not yet spawned keep their edge too, so
|
Agents that are approved but not yet spawned keep their edge too, so
|
||||||
it survives the gap until the container actually appears.
|
it survives the gap until the container actually appears.
|
||||||
- **Inject** — each container's parent (if any) is exposed to its own
|
- **Inject** — hive-c0re exposes each container's parent (if any) to its own
|
||||||
environment as `HIVE_PARENT`, so the harness / system-prompt
|
environment as `HIVE_PARENT`, so the harness / system-prompt
|
||||||
renderer can see it.
|
renderer can see it.
|
||||||
- **Surface** — every rescan re-reads `topology.json` and populates
|
- **Surface** — every rescan re-reads `topology.json` and populates
|
||||||
|
|
@ -98,8 +98,8 @@ umount-old / mount-new / restart-cascade step.
|
||||||
| `send` / `recv` routing | parent ↔ same-parent siblings ↔ self ↔ descendants; explicit allow-list for anyone else |
|
| `send` / `recv` routing | parent ↔ same-parent siblings ↔ self ↔ descendants; explicit allow-list for anyone else |
|
||||||
| `request_update_meta_inputs` (bump meta lock) | root agents only (today: just `manager`) |
|
| `request_update_meta_inputs` (bump meta lock) | root agents only (today: just `manager`) |
|
||||||
|
|
||||||
"Ancestor" walks `ContainerView.parent` chains; cycles are guarded by a
|
"Ancestor" walks `ContainerView.parent` chains; a visited-set guards against
|
||||||
visited-set at dispatch time (a malformed `topology.json` can't lock
|
cycles at dispatch time (a malformed `topology.json` can't lock
|
||||||
the dispatcher into a loop).
|
the dispatcher into a loop).
|
||||||
|
|
||||||
## Manager special-casing today
|
## Manager special-casing today
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ CLI) before it takes effect. What you'll see, and what to do with it:
|
||||||
|
|
||||||
Don't want to approve something? **Deny it** (`DENY` on the dashboard
|
Don't want to approve something? **Deny it** (`DENY` on the dashboard
|
||||||
card, or `hivectl approvals deny <id>`) — nothing runs. Either way the
|
card, or `hivectl approvals deny <id>`) — nothing runs. Either way the
|
||||||
submitting agent is always notified their request was denied; what's
|
submitting agent is always notified that the operator denied their request; what's
|
||||||
optional is only the reason text, which you can add on the dashboard's
|
optional is only the reason text, which you can add on the dashboard's
|
||||||
prompt (cancelling that prompt aborts the whole deny, not just the
|
prompt (cancelling that prompt aborts the whole deny, not just the
|
||||||
reason) but not from the CLI. Denying is final: a denied approval
|
reason) but not from the CLI. Denying is final: a denied approval
|
||||||
|
|
@ -76,8 +76,8 @@ request.
|
||||||
`MergeConfigPr` approval; a poll fallback catches any missed webhook.
|
`MergeConfigPr` approval; a poll fallback catches any missed webhook.
|
||||||
The approval row stores the PR **number** (`commit_ref`) and the PR
|
The approval row stores the PR **number** (`commit_ref`) and the PR
|
||||||
**head sha at queue time** (`fetched_sha` — the "reviewed" sha). If
|
**head sha at queue time** (`fetched_sha` — the "reviewed" sha). If
|
||||||
the PR head later moves, the stale approval is superseded by a fresh
|
the PR head later moves, a fresh approval pinned to the new head supersedes
|
||||||
one pinned to the new head, so the operator always reviews what will
|
the stale one, so the operator always reviews what will
|
||||||
actually deploy.
|
actually deploy.
|
||||||
3. The operator reviews the PR **on the forge** (native diff, threaded
|
3. The operator reviews the PR **on the forge** (native diff, threaded
|
||||||
comments, CI status) and sees a matching card on the dashboard with a
|
comments, CI status) and sees a matching card on the dashboard with a
|
||||||
|
|
@ -118,13 +118,13 @@ request.
|
||||||
### Withdrawing a pending approval
|
### Withdrawing a pending approval
|
||||||
|
|
||||||
The submitting agent can call `cancel_loose_end(kind: "approval", id)` to
|
The submitting agent can call `cancel_loose_end(kind: "approval", id)` to
|
||||||
withdraw an approval that hasn't been acted on yet.
|
withdraw an approval the operator hasn't acted on yet.
|
||||||
The row transitions to `ApprovalStatus::Cancelled` (distinct from
|
The row transitions to `ApprovalStatus::Cancelled` (distinct from
|
||||||
`Denied`/`Failed`), the dashboard pulls the card out of the
|
`Denied`/`Failed`), the dashboard pulls the card out of the
|
||||||
pending pane, and `ApprovalResolved { status: "cancelled" }` fires
|
pending pane, and `ApprovalResolved { status: "cancelled" }` fires
|
||||||
on the root agent + dashboard channels. Approvals that have already
|
on the root agent + dashboard channels. Approvals that the operator
|
||||||
been approved/denied/failed return an error — the resolution is
|
(or a lifecycle failure) has already approved, denied, or failed return an error — the resolution is
|
||||||
final once the operator (or a lifecycle failure) acted on the row.
|
final once acted on.
|
||||||
|
|
||||||
The socket refuses the `approval` kind with a clear error for any
|
The socket refuses the `approval` kind with a clear error for any
|
||||||
agent that lacks the `approvals` tool group: only an agent with that
|
agent that lacks the `approvals` tool group: only an agent with that
|
||||||
|
|
@ -178,7 +178,7 @@ kind-specific payload carrier.
|
||||||
operator approval. Step 1 of the two-step spawn flow above.
|
operator approval. Step 1 of the two-step spawn flow above.
|
||||||
- `UpdateMetaInputs` — `commit_ref` stores the JSON-encoded inputs
|
- `UpdateMetaInputs` — `commit_ref` stores the JSON-encoded inputs
|
||||||
array (`"[]"` = all inputs, `"[\"nixpkgs\"]"` = just nixpkgs,
|
array (`"[]"` = all inputs, `"[\"nixpkgs\"]"` = just nixpkgs,
|
||||||
etc.). `agent` field is set to the requesting root agent.
|
etc.). hive-c0re sets the `agent` field to the requesting root agent.
|
||||||
On approve hive-c0re runs `nix flake update [inputs...]` on the
|
On approve hive-c0re runs `nix flake update [inputs...]` on the
|
||||||
meta flake and commits the resulting lock changes.
|
meta flake and commits the resulting lock changes.
|
||||||
- `SchedulePrompt` — `commit_ref` stores the JSON-encoded
|
- `SchedulePrompt` — `commit_ref` stores the JSON-encoded
|
||||||
|
|
@ -194,15 +194,15 @@ kind-specific payload carrier.
|
||||||
Two ways a row lands in `scheduled_prompts`:
|
Two ways a row lands in `scheduled_prompts`:
|
||||||
|
|
||||||
- **Operator-direct** (`source = "operator"`): the operator adds a schedule through the dashboard form. Lands in the table immediately, no approval gate — operator action is already the trust boundary.
|
- **Operator-direct** (`source = "operator"`): the operator adds a schedule through the dashboard form. Lands in the table immediately, no approval gate — operator action is already the trust boundary.
|
||||||
- **Agent-requested** (`source = "approval:<id>"`): an agent submits a `RequestSchedulePrompt` through its MCP socket (the `request_schedule_prompt` tool, `scheduling` group). An `ApprovalKind::SchedulePrompt` row is queued; on approve, hive-c0re inserts the schedule row with `source = approval:<id>` so the audit trail points back at the operator decision (above).
|
- **Agent-requested** (`source = "approval:<id>"`): an agent submits a `RequestSchedulePrompt` through its MCP socket (the `request_schedule_prompt` tool, `scheduling` group). hive-c0re queues an `ApprovalKind::SchedulePrompt` row; on approve, it inserts the schedule row with `source = approval:<id>` so the audit trail points back at the operator decision (above).
|
||||||
|
|
||||||
No self-target shortcut: even agent-self schedules need approval. The existing `remind` MCP tool stays the quick self-wake path (no approval, lands directly in the agent's own inbox); this module is the bigger, multi-recipient, operator-visible thing.
|
No self-target shortcut: even agent-self schedules need approval. The existing `remind` MCP tool stays the quick self-wake path (no approval, lands directly in the agent's own inbox); this module is the bigger, multi-recipient, operator-visible thing.
|
||||||
|
|
||||||
### Scheduled prompt worker (catch-up clamp)
|
### Scheduled prompt worker (catch-up clamp)
|
||||||
|
|
||||||
When hive-c0re comes back from being down, the worker sees rows whose `next_fire_at_unix` is well in the past. For recurring rows that would mean firing N delayed pulses in a row — spammy and useless. Instead the worker fires **once** per row and bumps `next_fire_at_unix` to the next interval slot ≥ `now`, recording how many cycles were skipped in `last_result` (per-target). Operators see "fired late, caught up from 17 skipped" instead of 17 wake-up storms.
|
When hive-c0re comes back from being down, the worker sees rows whose `next_fire_at_unix` is well in the past. For recurring rows that would mean firing N delayed pulses in a row — spammy and useless. Instead the worker fires **once** per row and bumps `next_fire_at_unix` to the next interval slot ≥ `now`, recording how many cycles it skipped in `last_result` (per-target). Operators see "fired late, caught up from 17 skipped" instead of 17 wake-up storms.
|
||||||
|
|
||||||
One-shot rows fire once (if past due, on the next worker pass) and are deleted by the worker; recurring rows survive until cancelled.
|
The worker fires one-shot rows once (if past due, on the next worker pass) and deletes them; recurring rows survive until cancelled.
|
||||||
|
|
||||||
`targets` is its own table (`scheduled_prompt_targets`) so partial cancellation flips a single row and the dashboard can show last-fired / last-result per recipient. Cancelling every target reaps the parent row on the next worker pass.
|
`targets` is its own table (`scheduled_prompt_targets`) so partial cancellation flips a single row and the dashboard can show last-fired / last-result per recipient. Cancelling every target reaps the parent row on the next worker pass.
|
||||||
|
|
||||||
|
|
@ -239,7 +239,7 @@ Transient broker errors (sqlite lock contention, etc.) get the same
|
||||||
|
|
||||||
- **Recurring rows** re-arm to the next interval slot — the retry
|
- **Recurring rows** re-arm to the next interval slot — the retry
|
||||||
self-heals on the next worker pass.
|
self-heals on the next worker pass.
|
||||||
- **One-shot rows** are deleted unconditionally after their single
|
- **One-shot rows**: the worker deletes them unconditionally after their single
|
||||||
fan-out pass; a broker error on a one-shot isn't retried (the
|
fan-out pass; a broker error on a one-shot isn't retried (the
|
||||||
operator advisory and `last_result` are the only audit trail).
|
operator advisory and `last_result` are the only audit trail).
|
||||||
|
|
||||||
|
|
@ -253,19 +253,18 @@ container, for example `/agents/<name>/state/foo.md`). On delivery hive-c0re:
|
||||||
from outside the container.
|
from outside the container.
|
||||||
2. **Validates** the path: rejects anything outside the agent's own state
|
2. **Validates** the path: rejects anything outside the agent's own state
|
||||||
subtree, containing `..` (path traversal), or with an empty relative
|
subtree, containing `..` (path traversal), or with an empty relative
|
||||||
tail. On rejection the write is skipped and the original message is
|
tail. On rejection hive-c0re skips the write and delivers the
|
||||||
delivered inline with a warning — the reminder still fires.
|
original message inline with a warning — the reminder still fires.
|
||||||
3. **Defends against symlink escape**: after `create_dir_all`, the parent
|
3. **Defends against symlink escape**: after `create_dir_all`, hive-c0re
|
||||||
dir is canonicalized and re-verified to live under the agent's host
|
canonicalizes the parent dir and re-verifies it lives under the agent's host
|
||||||
state root. The final file is opened with
|
state root. hive-c0re opens the final file with
|
||||||
`O_NOFOLLOW | O_CREAT | O_TRUNC` so an existing symlink at the
|
`O_NOFOLLOW | O_CREAT | O_TRUNC` so an existing symlink at the
|
||||||
basename can't redirect the write to an arbitrary host path.
|
basename can't redirect the write to an arbitrary host path.
|
||||||
4. **Writes the body to disk** and delivers a short pointer message in its
|
4. **Writes the body to disk** and delivers a short pointer message in its
|
||||||
place, keeping the agent's inbox / wake-prompt small while the bulky
|
place, keeping the agent's inbox / wake-prompt small while the agent
|
||||||
payload is read out of band.
|
reads the bulky payload out of band.
|
||||||
|
|
||||||
Atomicity of the inbox INSERT + `reminders.sent_at` UPDATE is handled
|
`Broker::deliver_reminders_batch` handles atomicity of the inbox INSERT + `reminders.sent_at` UPDATE; the scheduler only computes the
|
||||||
inside `Broker::deliver_reminders_batch`; the scheduler only computes the
|
|
||||||
body strings before calling it.
|
body strings before calling it.
|
||||||
|
|
||||||
### Destroy semantics
|
### Destroy semantics
|
||||||
|
|
@ -354,7 +353,7 @@ from the agent list; if it differs from disk, runs
|
||||||
The root agent has `/meta` RO-bound inside its container:
|
The root agent has `/meta` RO-bound inside its container:
|
||||||
`git -C /meta log --oneline` is the swarm-wide deploy log,
|
`git -C /meta log --oneline` is the swarm-wide deploy log,
|
||||||
`cat /meta/flake.lock | jq '.nodes["agent-<n>"].locked'`
|
`cat /meta/flake.lock | jq '.nodes["agent-<n>"].locked'`
|
||||||
resolves which sha each agent is pinned at right now.
|
resolves which sha the flake pins each agent at right now.
|
||||||
Dashboard surfaces the same info as a `deployed:<sha12>` chip
|
Dashboard surfaces the same info as a `deployed:<sha12>` chip
|
||||||
per container row.
|
per container row.
|
||||||
|
|
||||||
|
|
@ -409,7 +408,7 @@ repo:
|
||||||
| `deployed/<id>` | rebuild succeeded — `main` ff's here | no |
|
| `deployed/<id>` | rebuild succeeded — `main` ff's here | no |
|
||||||
| `failed/<id>` | rebuild failed | yes (body = error) |
|
| `failed/<id>` | rebuild failed | yes (body = error) |
|
||||||
|
|
||||||
`deployed/0` is planted at first spawn. `applied/main` is always the
|
hive-c0re plants `deployed/0` at first spawn. `applied/main` is always the
|
||||||
latest `deployed/*`. A `failed/` tree stays browsable forever — `git log
|
latest `deployed/*`. A `failed/` tree stays browsable forever — `git log
|
||||||
--tags` in the applied repo is the audit trail. A denied or failed config
|
--tags` in the applied repo is the audit trail. A denied or failed config
|
||||||
PR carries no extra state on the forge side: the PR stays open, and the
|
PR carries no extra state on the forge side: the PR stays open, and the
|
||||||
|
|
@ -477,8 +476,8 @@ repo — so it can push a branch and open a config PR — but not a member
|
||||||
of any other agent's, so it can't reach another agent's config through
|
of any other agent's, so it can't reach another agent's config through
|
||||||
the forge. Branch protection keeps `main` push/merge `core`-only with
|
the forge. Branch protection keeps `main` push/merge `core`-only with
|
||||||
operator-team approval, so an agent can't fast-forward its own config or
|
operator-team approval, so an agent can't fast-forward its own config or
|
||||||
self-merge its PR (see the End-to-end flow + #1787). The tokenised push
|
self-merge its PR (see the End-to-end flow + #1787). hive-c0re passes the tokenised push
|
||||||
URL is passed inline to `git push`, never written into
|
URL inline to `git push`, never writing it into
|
||||||
`applied/<n>/.git/config`; that repo is RO-bind-mounted into the root
|
`applied/<n>/.git/config`; that repo is RO-bind-mounted into the root
|
||||||
agent, and a stored token would leak core's admin credential to an
|
agent, and a stored token would leak core's admin credential to an
|
||||||
agent.
|
agent.
|
||||||
|
|
@ -497,7 +496,7 @@ for each entry in
|
||||||
current config — not an editing surface.
|
current config — not an editing surface.
|
||||||
|
|
||||||
An agent with the `approvals` tool group submits a change the same way
|
An agent with the `approvals` tool group submits a change the same way
|
||||||
any other change is made: **clone the child's config repo from the
|
it makes any other change: **clone the child's config repo from the
|
||||||
forge into its own state dir, commit on a branch, open a PR**, and let
|
forge into its own state dir, commit on a branch, open a PR**, and let
|
||||||
the operator review and approve it. By design, no second, mount-shaped
|
the operator review and approve it. By design, no second, mount-shaped
|
||||||
path reaches the same file without the review.
|
path reaches the same file without the review.
|
||||||
|
|
@ -543,7 +542,7 @@ host set up before the tag-driven-deploy + meta-flake scheme (both
|
||||||
described above) converges to it automatically. Each phase is a no-op
|
described above) converges to it automatically. Each phase is a no-op
|
||||||
once already applied:
|
once already applied:
|
||||||
|
|
||||||
- **Tags**: agents from before the tag-driven scheme are tagged
|
- **Tags**: hive-c0re tags agents from before the tag-driven scheme
|
||||||
`deployed/0` on `main` once. Non-destructive — it doesn't touch live
|
`deployed/0` on `main` once. Non-destructive — it doesn't touch live
|
||||||
containers, state dirs, or claude creds.
|
containers, state dirs, or claude creds.
|
||||||
- **Meta flake**: rewrites each `applied/<n>/flake.nix` to the
|
- **Meta flake**: rewrites each `applied/<n>/flake.nix` to the
|
||||||
|
|
@ -579,7 +578,7 @@ Differences from sub-agents:
|
||||||
authoritative applied repo (see "Root-agent view of applied" below).
|
authoritative applied repo (see "Root-agent view of applied" below).
|
||||||
- First-deploy spawn bypasses the approval queue (the root agent is
|
- First-deploy spawn bypasses the approval queue (the root agent is
|
||||||
required infrastructure).
|
required infrastructure).
|
||||||
- The root agent's socket is bound by `socket_server::start_manager`,
|
- `socket_server::start_manager` binds the root agent's socket,
|
||||||
pure transport with no dedicated helpers — it uses the same
|
pure transport with no dedicated helpers — it uses the same
|
||||||
per-agent runtime dir as any other agent (`/run/hyperhive/agents/ruth/`),
|
per-agent runtime dir as any other agent (`/run/hyperhive/agents/ruth/`),
|
||||||
not a special manager-only path.
|
not a special manager-only path.
|
||||||
|
|
@ -592,7 +591,7 @@ updates the root agent itself.
|
||||||
|
|
||||||
The system prompt (`hive-agent/prompts/system.md`, rendered by
|
The system prompt (`hive-agent/prompts/system.md`, rendered by
|
||||||
`hive-agent/src/prompt.rs`) is the **same for every agent**; what
|
`hive-agent/src/prompt.rs`) is the **same for every agent**; what
|
||||||
varies is which MCP tools are surfaced (gated by tool groups and
|
varies is which MCP tools it surfaces (gated by tool groups and
|
||||||
capabilities in `agent.nix`). No `role:manager` block renders only
|
capabilities in `agent.nix`). No `role:manager` block renders only
|
||||||
for the root agent. The root agent's approval-gating
|
for the root agent. The root agent's approval-gating
|
||||||
behaviour comes from its CLAUDE.md / agent-specific instructions, not
|
behaviour comes from its CLAUDE.md / agent-specific instructions, not
|
||||||
|
|
@ -648,7 +647,7 @@ commit (spawn / init_config / meta-update / deny, and the autoupdate
|
||||||
sweep's `job_queue::templates::rebuild` reapplying the existing main,
|
sweep's `job_queue::templates::rebuild` reapplying the existing main,
|
||||||
or the dashboard `↻ R3BU1LD` button when the lock didn't move). When set,
|
or the dashboard `↻ R3BU1LD` button when the lock didn't move). When set,
|
||||||
`git show <sha>` against `/applied/<n>/.git` inside the
|
`git show <sha>` against `/applied/<n>/.git` inside the
|
||||||
bootstrap container yields the exact tree that was referenced.
|
bootstrap container yields the exact tree the sha referenced.
|
||||||
|
|
||||||
To add a new lifecycle notice: if it needs to drive an immediate turn
|
To add a new lifecycle notice: if it needs to drive an immediate turn
|
||||||
(something genuinely urgent, like `ContainerCrash`), add a
|
(something genuinely urgent, like `ContainerCrash`), add a
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ power-intent registry:
|
||||||
next_fire_at_unix / created_at_unix / source ("operator" or
|
next_fire_at_unix / created_at_unix / source ("operator" or
|
||||||
"approval:<id>") / cancelled_at_unix / description`. `owner`
|
"approval:<id>") / cancelled_at_unix / description`. `owner`
|
||||||
drives cancel-permission checks (operator vs the submitting
|
drives cancel-permission checks (operator vs the submitting
|
||||||
agent). Cancelled rows are tombstoned and reaped by the worker
|
agent). The worker tombstones and reaps cancelled rows
|
||||||
on its next pass.
|
on its next pass.
|
||||||
- `scheduled_prompt_targets` — per-target state for each schedule.
|
- `scheduled_prompt_targets` — per-target state for each schedule.
|
||||||
`schedule_id / target / cancelled_at_unix /
|
`schedule_id / target / cancelled_at_unix /
|
||||||
|
|
@ -87,16 +87,16 @@ Retention:
|
||||||
`hive-c0re::main`. Drops acked message rows older than 30 days
|
`hive-c0re::main`. Drops acked message rows older than 30 days
|
||||||
(`acked_at IS NOT NULL`). Undelivered + delivered-but-not-acked
|
(`acked_at IS NOT NULL`). Undelivered + delivered-but-not-acked
|
||||||
rows are always kept — the harness `ack_turn`s only after a
|
rows are always kept — the harness `ack_turn`s only after a
|
||||||
successful turn, so an unacked row can still be requeued via
|
successful turn, so `requeue_inflight` can still requeue
|
||||||
`requeue_inflight` on a crash.
|
an unacked row on a crash.
|
||||||
- Approvals are kept indefinitely — an audit trail. `actions::destroy`
|
- Approvals are kept indefinitely — an audit trail. `actions::destroy`
|
||||||
rows stay visible to anything that queries by id.
|
rows stay visible to anything that queries by id.
|
||||||
- Scheduled prompts: one-shot rows are deleted on fire by the
|
- Scheduled prompts: the worker deletes one-shot rows on fire;
|
||||||
worker; recurring rows live until the operator cancels them
|
recurring rows live until the operator cancels them
|
||||||
(`cancel_schedule` MCP / dashboard ✗) which tombstones via
|
(`cancel_schedule` MCP / dashboard ✗) which tombstones via
|
||||||
`cancelled_at_unix`, then `reap_cancelled` drops the row on
|
`cancelled_at_unix`, then `reap_cancelled` drops the row on
|
||||||
the next worker pass.
|
the next worker pass.
|
||||||
- `agent_power` rows live until the agent is destroyed (one row per
|
- `agent_power` rows live until the operator destroys the agent (one row per
|
||||||
agent — nothing to vacuum).
|
agent — nothing to vacuum).
|
||||||
|
|
||||||
### `/harness/hyperhive-events.sqlite` (per agent)
|
### `/harness/hyperhive-events.sqlite` (per agent)
|
||||||
|
|
@ -148,9 +148,9 @@ Bin-loop helpers `build_row` + `record` land each row at
|
||||||
`turn_end`; writes are best-effort, a sqlite hiccup logs + lets
|
`turn_end`; writes are best-effort, a sqlite hiccup logs + lets
|
||||||
the turn loop continue.
|
the turn loop continue.
|
||||||
|
|
||||||
A sibling `bash_commands(ts INTEGER, head TEXT)` table in the same
|
The `hive-bash-daemon` (not the harness) writes a sibling
|
||||||
file is written by the `hive-bash-daemon` (not the harness): one
|
`bash_commands(ts INTEGER, head TEXT)` table in the same
|
||||||
row per executed bash task recording the normalised command head -
|
file: one row per executed bash task recording the normalised command head -
|
||||||
the basename of the first real command, looking past `cd repo &&`
|
the basename of the first real command, looking past `cd repo &&`
|
||||||
prefixes, env-assignments, and prefix-runners like `sudo`/`env`. It
|
prefixes, env-assignments, and prefix-runners like `sudo`/`env`. It
|
||||||
backs the "favorite tools" view on the /stats page (aggregated
|
backs the "favorite tools" view on the /stats page (aggregated
|
||||||
|
|
@ -215,7 +215,7 @@ Three indices:
|
||||||
as part of the existing hourly vacuum.
|
as part of the existing hourly vacuum.
|
||||||
- `(node_id)` — added by a later migration so a build log row can be
|
- `(node_id)` — added by a later migration so a build log row can be
|
||||||
looked up by the job-queue node it belongs to (a `hive_jobq` node is
|
looked up by the job-queue node it belongs to (a `hive_jobq` node is
|
||||||
immutable after insert, so the link is recorded on the log row
|
immutable after insert, so hive-c0re records the link on the log row
|
||||||
instead); legacy rows predating the column keep `node_id IS NULL`.
|
instead); legacy rows predating the column keep `node_id IS NULL`.
|
||||||
|
|
||||||
Writes are best-effort: `append_stdout` / `append_stderr` / `finish`
|
Writes are best-effort: `append_stdout` / `append_stderr` / `finish`
|
||||||
|
|
@ -313,15 +313,15 @@ Under `/var/lib/hyperhive/agents/<name>/`:
|
||||||
store + wake `Notify` directly rather than dialling its own socket.
|
store + wake `Notify` directly rather than dialling its own socket.
|
||||||
`disk_watch` raises a keyed `disk` todo when the filesystem backing
|
`disk_watch` raises a keyed `disk` todo when the filesystem backing
|
||||||
this agent's state gets tight, naming the agent's own biggest
|
this agent's state gets tight, naming the agent's own biggest
|
||||||
directories; the summary is bucketed and carries no raw byte
|
directories; it buckets the summary and carries no raw byte
|
||||||
counts, so an unchanged situation re-upserts as `changed == false`
|
counts, so an unchanged situation re-upserts as `changed == false`
|
||||||
and never re-wakes.
|
and never re-wakes.
|
||||||
|
|
||||||
Retention, same hourly `hive-agent::vacuum::run` sweep as
|
Retention, same hourly `hive-agent::vacuum::run` sweep as
|
||||||
`hyperhive-events.sqlite` below: delivered (soft-deleted) reminder
|
`hyperhive-events.sqlite` below: it reaps delivered (soft-deleted) reminder
|
||||||
rows are reaped 14 days after delivery, kept that long only to serve
|
rows 14 days after delivery, kept that long only to serve
|
||||||
the trailing-window `ReminderRollup` stats; acked todo rows are
|
the trailing-window `ReminderRollup` stats, and reaps acked todo rows
|
||||||
reaped 30 days after acking (long enough that only a genuinely quiet
|
30 days after acking (long enough that only a genuinely quiet
|
||||||
month triggers the "one spurious re-announcement" fallback a
|
month triggers the "one spurious re-announcement" fallback a
|
||||||
reconciled producer like `disk_watch` relies on — see `todos.rs`'s
|
reconciled producer like `disk_watch` relies on — see `todos.rs`'s
|
||||||
module doc). Un-acked todos and undelivered reminders are never
|
module doc). Un-acked todos and undelivered reminders are never
|
||||||
|
|
@ -371,8 +371,8 @@ files committed via the approval flow. `.git/` carries the proposal /
|
||||||
approved / building / deployed / failed / denied tag history.
|
approved / building / deployed / failed / denied tag history.
|
||||||
|
|
||||||
Under `/var/lib/hyperhive/meta/` — the swarm-wide deploy flake plus
|
Under `/var/lib/hyperhive/meta/` — the swarm-wide deploy flake plus
|
||||||
system-level config files. Single git repo for the whole host; every
|
system-level config files. Single git repo for the whole host; hive-c0re
|
||||||
hive-c0re mutation that should survive a restart is committed here.
|
commits every mutation that should survive a restart here.
|
||||||
Contents:
|
Contents:
|
||||||
|
|
||||||
- `flake.nix` — declares one `nixpkgs` input per agent + one
|
- `flake.nix` — declares one `nixpkgs` input per agent + one
|
||||||
|
|
@ -396,8 +396,8 @@ Contents:
|
||||||
var. Absent agents have no extra capabilities.
|
var. Absent agents have no extra capabilities.
|
||||||
- `resource-limits.json` — per-agent container resource overrides
|
- `resource-limits.json` — per-agent container resource overrides
|
||||||
(`{ "sock": { "cpu_quota": "400%", "memory_max": "8G" } }`).
|
(`{ "sock": { "cpu_quota": "400%", "memory_max": "8G" } }`).
|
||||||
Written by `resource_limits::set_limits`; read where the systemd
|
Written by `resource_limits::set_limits`; read where
|
||||||
drop-in is generated (`lifecycle::write_dropins`), **not** injected
|
`lifecycle::write_dropins` generates the systemd drop-in, **not** injected
|
||||||
into the container — these are host-side caps on the container, so
|
into the container — these are host-side caps on the container, so
|
||||||
the capped party never sees or sets them. Fallback is per *field*:
|
the capped party never sees or sets them. Fallback is per *field*:
|
||||||
an absent file, absent agent, or absent field falls back to the
|
an absent file, absent agent, or absent field falls back to the
|
||||||
|
|
@ -411,7 +411,7 @@ The root agent has the meta dir RO-mounted at `/meta/`.
|
||||||
|
|
||||||
The `.meta-migration-done` marker no longer exists: the
|
The `.meta-migration-done` marker no longer exists: the
|
||||||
one-shot container repoint it guarded has been removed, since
|
one-shot container repoint it guarded has been removed, since
|
||||||
containers are rendered onto `meta#<n>` at creation. A stale
|
hive-c0re renders containers onto `meta#<n>` at creation. A stale
|
||||||
marker file left over from an older hive is inert and can be
|
marker file left over from an older hive is inert and can be
|
||||||
deleted.
|
deleted.
|
||||||
|
|
||||||
|
|
@ -426,8 +426,8 @@ an agent's state. The mechanics, for completeness:
|
||||||
- `PURG3` wipes `/var/lib/hyperhive/{agents,applied}/<name>/` — the
|
- `PURG3` wipes `/var/lib/hyperhive/{agents,applied}/<name>/` — the
|
||||||
union of everything `DESTR0Y` left behind.
|
union of everything `DESTR0Y` left behind.
|
||||||
|
|
||||||
The root/bootstrap agent's specialness is implemented as a soft policy
|
`actions::destroy` implements the root/bootstrap agent's specialness as a soft policy
|
||||||
guard in `actions::destroy` that refuses to destroy it, backstopped by
|
guard that refuses to destroy it, backstopped by
|
||||||
`auto_update::ensure_root_agent`, which recreates it on the next
|
`auto_update::ensure_root_agent`, which recreates it on the next
|
||||||
hive-c0re startup if it's ever absent (bypassing the approval queue,
|
hive-c0re startup if it's ever absent (bypassing the approval queue,
|
||||||
as required infrastructure) — so even without the guard, destroying it
|
as required infrastructure) — so even without the guard, destroying it
|
||||||
|
|
@ -435,7 +435,7 @@ would only be transient.
|
||||||
|
|
||||||
### btrfs subvolumes for `/var/lib/hyperhive/agents/<name>`
|
### btrfs subvolumes for `/var/lib/hyperhive/agents/<name>`
|
||||||
|
|
||||||
On a btrfs host, a brand-new agent's state root is created as a
|
On a btrfs host, `lifecycle::ensure_agent_state_subvolume` creates a brand-new agent's state root as a
|
||||||
**btrfs subvolume** instead of a plain directory (progressive
|
**btrfs subvolume** instead of a plain directory (progressive
|
||||||
enhancement — see the #1762 lane). This is a no-op fallback on
|
enhancement — see the #1762 lane). This is a no-op fallback on
|
||||||
non-btrfs hosts and for any agent whose root already exists, so
|
non-btrfs hosts and for any agent whose root already exists, so
|
||||||
|
|
@ -443,7 +443,7 @@ nothing is automigrated: existing agents keep their plain dirs
|
||||||
until an explicit opt-in upgrade.
|
until an explicit opt-in upgrade.
|
||||||
|
|
||||||
- **Creation:** `lifecycle::ensure_agent_state_subvolume` runs before
|
- **Creation:** `lifecycle::ensure_agent_state_subvolume` runs before
|
||||||
the per-agent subdirs are created (spawn / rebuild / InitConfig).
|
hive-c0re creates the per-agent subdirs (spawn / rebuild / InitConfig).
|
||||||
It skips the work when the root already exists; otherwise it asks
|
It skips the work when the root already exists; otherwise it asks
|
||||||
hive-priv (`EnsureAgentSubvolume`) to `btrfs subvolume create` the
|
hive-priv (`EnsureAgentSubvolume`) to `btrfs subvolume create` the
|
||||||
root when the FS is btrfs (`statfs` magic gate) and chown it to the
|
root when the FS is btrfs (`statfs` magic gate) and chown it to the
|
||||||
|
|
@ -475,8 +475,8 @@ Only present on the one host running
|
||||||
`services.hyperhive.deploy.swarm-controller.enable`. systemd `StateDirectory=`,
|
`services.hyperhive.deploy.swarm-controller.enable`. systemd `StateDirectory=`,
|
||||||
so it survives restarts and redeploys.
|
so it survives restarts and redeploys.
|
||||||
|
|
||||||
- `webhook-secret` — the HMAC key the swarm's forge webhooks are signed
|
- `webhook-secret` — the HMAC key Forgejo signs the swarm's forge webhooks
|
||||||
with. **Keep it.** It's handed to Forgejo when a hook is registered,
|
with. **Keep it.** It's handed to Forgejo when swarm-controller registers a hook,
|
||||||
so replacing the file means every subsequent delivery fails
|
so replacing the file means every subsequent delivery fails
|
||||||
verification until the hook is re-registered with the new value. It's
|
verification until the hook is re-registered with the new value. It's
|
||||||
generated automatically on first start; there is nothing to configure.
|
generated automatically on first start; there is nothing to configure.
|
||||||
|
|
@ -565,8 +565,8 @@ avatar (see below).
|
||||||
|
|
||||||
### matrix avatar (set by the daemon over the live Client)
|
### matrix avatar (set by the daemon over the live Client)
|
||||||
|
|
||||||
The agent icon (`hyperhive.icon`, an SVG) is published as each matrix
|
`hive-matrix-daemon` itself publishes the agent icon (`hyperhive.icon`, an SVG) as each matrix
|
||||||
account's profile avatar by `hive-matrix-daemon` itself
|
account's profile avatar
|
||||||
(`hive-matrix-mcp::client::sync_avatar`), not a separate oneshot. After
|
(`hive-matrix-mcp::client::sync_avatar`), not a separate oneshot. After
|
||||||
the daemon builds + restores an account's `Client` (authenticated,
|
the daemon builds + restores an account's `Client` (authenticated,
|
||||||
pointed at that account's resolved homeserver), it calls matrix-sdk's
|
pointed at that account's resolved homeserver), it calls matrix-sdk's
|
||||||
|
|
@ -574,7 +574,7 @@ pointed at that account's resolved homeserver), it calls matrix-sdk's
|
||||||
`avatar_url`. Because it reuses the live Client, there is no hardcoded
|
`avatar_url`. Because it reuses the live Client, there is no hardcoded
|
||||||
homeserver URL, no token re-read, and no token-file globbing: the daemon
|
homeserver URL, no token re-read, and no token-file globbing: the daemon
|
||||||
already iterates every configured + dashboard-discovered account in its
|
already iterates every configured + dashboard-discovered account in its
|
||||||
bring-up loop, so the avatar is set for **every** account.
|
bring-up loop, so it sets the avatar for **every** account.
|
||||||
|
|
||||||
Nix rasterizes the SVG to a 512x512 PNG at build time (`iconPng`, via
|
Nix rasterizes the SVG to a 512x512 PNG at build time (`iconPng`, via
|
||||||
librsvg) and forwards its store path as `HIVE_ICON_PNG` on the daemon
|
librsvg) and forwards its store path as `HIVE_ICON_PNG` on the daemon
|
||||||
|
|
@ -588,6 +588,6 @@ skips the upload when unchanged, because every upload mints a fresh
|
||||||
re-uploading identical bytes is timeline spam. A dashboard-provisioned
|
re-uploading identical bytes is timeline spam. A dashboard-provisioned
|
||||||
account gets its avatar when the `systemd.paths.hive-matrix-daemon` token
|
account gets its avatar when the `systemd.paths.hive-matrix-daemon` token
|
||||||
watcher restarts the daemon (which re-runs the per-account bring-up), so
|
watcher restarts the daemon (which re-runs the per-account bring-up), so
|
||||||
no separate avatar trigger is needed. Avatar failures are swallowed
|
no separate avatar trigger is needed. The daemon swallows avatar failures
|
||||||
(logged, non-fatal) so they never break account bring-up or sync.
|
(logged, non-fatal) so they never break account bring-up or sync.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue