drop the proposed config repo: applied seeds from forge main, and the dead pending-init topology list goes with it
This commit is contained in:
parent
08b8eb3f73
commit
25295fd33f
6 changed files with 225 additions and 520 deletions
|
|
@ -102,7 +102,7 @@ body strings before calling it.
|
|||
|
||||
`HostRequest::Destroy { name, purge }` is the lifecycle tear-down.
|
||||
Stops + removes the nspawn container and drops the systemd drop-in.
|
||||
Persistent state (proposed/applied repos, claude credentials, `/state/`
|
||||
Persistent state (the applied repo, 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
|
||||
|
|
@ -186,20 +186,10 @@ the swarm builder built the agent from the updated repo (the
|
|||
reconcile ([below](#autoupdate-on-startup)) and the dashboard's update chip
|
||||
read them.
|
||||
|
||||
## Two repos per agent
|
||||
## The applied repo per agent
|
||||
|
||||
```
|
||||
/var/lib/hyperhive/agents/<name>/config/ proposed — parent mount is RO
|
||||
└── <anything> # any files the submitting
|
||||
# agent wants in the commit.
|
||||
# agent.nix is the
|
||||
# convention entry
|
||||
# point; flake.nix is
|
||||
# tracked boilerplate
|
||||
# (submitting agent doesn't
|
||||
# edit it).
|
||||
|
||||
/var/lib/hyperhive/applied/<name>/ applied — core-only
|
||||
/var/lib/hyperhive/applied/<name>/ applied - core-only
|
||||
├── .git/ # tag-rich history
|
||||
├── flake.nix # tracked, fixed
|
||||
│ # boilerplate exporting
|
||||
|
|
@ -207,16 +197,18 @@ the swarm builder built the agent from the updated repo (the
|
|||
├── agent.nix # working tree of main
|
||||
└── <other committed files> # also tracked
|
||||
|
||||
/var/lib/hyperhive/meta/ plain hive state — core
|
||||
/var/lib/hyperhive/meta/ plain hive state - core
|
||||
├── topology.json # the live agent set
|
||||
└── swarm-resource-limits.json # the swarm's per-agent caps
|
||||
```
|
||||
|
||||
Why two physical repos: the submitting agent's `/agents/<n>/config/` is
|
||||
RW — a buggy or hostile agent can `git clean -fdx` its own
|
||||
proposed tree. The applied repo is never bind-mounted (except
|
||||
the read-only `.git` exposure described below) so a destructive
|
||||
move inside the container can't reach it.
|
||||
hive-c0re seeds the applied repo at first spawn from the forge's
|
||||
`agent-configs/<name>` `main`, or from a template when the forge has none yet,
|
||||
and fast-forwards it to each merged config commit that deploys. An agent never
|
||||
edits it: the agent sees it read-only at `/agents/<name>/config`, so it can
|
||||
read what governs it, while a config change is a PR against its forge repo.
|
||||
Older hives also kept a *proposed* clone at `agents/<name>/config`. Nothing
|
||||
reads it any more, and you can delete it.
|
||||
|
||||
The container's `--flake` ref is `internal/meta` (see "Meta flake" above).
|
||||
The agent's own `applied/<n>/flake.nix`
|
||||
|
|
@ -240,9 +232,8 @@ Every agent created through the swarm has a repo in the private
|
|||
`agent-configs` Forgejo org, created and seeded by swarm-controller.
|
||||
hive-c0re pushes nothing to it: the applied repo's tags (`deployed/0`)
|
||||
exist only locally, and forge `main` advances only by an operator's merge.
|
||||
hive-c0re reads from it — reseeding a purged `applied`
|
||||
(`forge::fetch_config_main_into_applied`), catching `applied` up, and
|
||||
seeding `proposed` at first spawn (`forge::clone_config_into_proposed`).
|
||||
hive-c0re reads from it - reseeding a purged `applied`, catching `applied` up, and
|
||||
and seeding `applied` at first spawn (`forge::fetch_config_main_into_applied`).
|
||||
|
||||
A legacy agent spawned by the hive alone has no swarm-seeded repo; if its
|
||||
`agent-configs/<name>` has no `main`, a first spawn falls back to the local
|
||||
|
|
@ -284,9 +275,9 @@ the swarm's `agent-permissions` record; see
|
|||
`docs/process/conventions.md#permissions`) get additional host-side bind
|
||||
mounts via `set_nspawn_flags`:
|
||||
|
||||
- `/var/lib/hyperhive/agents/` → `/agents/` (RW) — **every** agent's
|
||||
proposed repo, not just direct children. The permission means "may
|
||||
manage any agent", so the mounted set is every agent.
|
||||
- `/var/lib/hyperhive/agents/<n>/state` → `/agents/<n>/state` (RW) for
|
||||
**every** agent, and `/agents/<n>/config` (RO, the applied repo). The
|
||||
permission means "may manage any agent," so the mounted set is every agent.
|
||||
- `/var/lib/hyperhive/applied/` → `/applied/` (RO) — every agent's
|
||||
authoritative applied repo, including `.git`.
|
||||
- `/var/lib/hyperhive/meta/` → `/meta/` (RO) — the hive's plain state
|
||||
|
|
@ -306,17 +297,13 @@ permissions were unset, swarm-controller writes the default plus
|
|||
`manage_root_agent` into her key once at start, so she keeps her mounts;
|
||||
it never touches a key that's already set.
|
||||
|
||||
Each proposed repo (`/agents/<n>/config/`) is pre-configured
|
||||
with `applied` as a git remote pointing at
|
||||
`/applied/<n>/.git`. Useful incantations from inside an agent with
|
||||
the full `/applied` mount:
|
||||
With the full `/applied` mount, an agent can read what hive-c0re deployed,
|
||||
failed or denied for any agent:
|
||||
|
||||
```sh
|
||||
git -C /agents/<n>/config fetch applied
|
||||
git -C /agents/<n>/config log applied/main --oneline
|
||||
git -C /agents/<n>/config show applied/refs/tags/deployed/0 # the seed commit
|
||||
git -C /agents/<n>/config show applied/refs/tags/denied/<id> # body = operator note
|
||||
git -C /agents/<n>/config rebase applied/main # base in-flight work on what's deployed
|
||||
git -C /applied/<n> log main --oneline
|
||||
git -C /applied/<n> show refs/tags/deployed/0 # the seed commit
|
||||
git -C /applied/<n> show refs/tags/denied/<id> # body = operator note
|
||||
```
|
||||
|
||||
The RO binds block push at the kernel level — git plumbing inside the
|
||||
|
|
@ -333,17 +320,16 @@ once already applied:
|
|||
`deployed/0` on `main` once. Non-destructive — it doesn't touch live
|
||||
containers, state dirs, or claude creds.
|
||||
- **Agent files**: rewrites each `applied/<n>/flake.nix` to the
|
||||
module-only boilerplate, wires the `applied` remote in each proposed
|
||||
repo, and refreshes the files that follow the agent set
|
||||
module-only boilerplate and refreshes the files that follow the agent set
|
||||
(`agent_set::refresh`) from the current agent list. Set
|
||||
`HIVE_SKIP_META_MIGRATION=1` on the service to defer this phase.
|
||||
- **Legacy meta repo**: deletes the leftover `.git` in
|
||||
`/var/lib/hyperhive/meta/`, the `meta` remote in each proposed repo and the
|
||||
`/var/lib/hyperhive/meta/` and the
|
||||
unread `/var/lib/hyperhive/forge-core-token`. The files beside the `.git`
|
||||
stay. The forge-side `core/meta` repo and `core` account aren't touched.
|
||||
|
||||
No state loss in either migration: claude creds, `/state/` notes, the
|
||||
events DB, and both proposed + applied history all survive. The root
|
||||
events DB, and the applied history all survive. The root
|
||||
agent keeps its session; sub-agents stay logged in.
|
||||
|
||||
## The root/bootstrap container
|
||||
|
|
@ -352,8 +338,7 @@ The root agent container runs through the **same lifecycle as
|
|||
sub-agents**. Nothing creates it at hive boot: you create ruth from the
|
||||
swarm dashboard like any agent, and an existing ruth container starts with
|
||||
the hive like every other. The root agent's flake lives at
|
||||
`/var/lib/hyperhive/applied/ruth/`; its proposed config at
|
||||
`/var/lib/hyperhive/agents/ruth/config/`. The root agent can edit its own
|
||||
`/var/lib/hyperhive/applied/ruth/`. The root agent can read its own
|
||||
`agent.nix` (visible inside the container at `/agents/ruth/config/`)
|
||||
and open a config PR on `agent-configs/ruth` for operator review,
|
||||
same as any other agent.
|
||||
|
|
@ -368,8 +353,7 @@ Differences from sub-agents:
|
|||
FNV-1a hash as every other agent (8100..8999 range).
|
||||
- The root agent holds `manage_root_agent` (the swarm UI's setup
|
||||
assistant creates her with it), for which `set_nspawn_flags` adds two extra binds:
|
||||
`/var/lib/hyperhive/agents` → `/agents` (RW) so the root agent can edit
|
||||
per-agent proposed repos,
|
||||
`/var/lib/hyperhive/agents` → `/agents` (RW) for every agent's state,
|
||||
and `/var/lib/hyperhive/applied` → `/applied` (RO) so the root agent
|
||||
can `git fetch` deployed/failed/denied tags from any agent's
|
||||
authoritative applied repo (see "Root-agent view of applied" below).
|
||||
|
|
|
|||
|
|
@ -254,11 +254,9 @@ than an accident of storage.
|
|||
|
||||
Under `/var/lib/hyperhive/agents/<name>/`:
|
||||
|
||||
- `config/` — the proposed nix repo (root-agent-editable). Bind-mounted
|
||||
**read-only** to `/agents/<name>/config` inside the sub-agent's own
|
||||
container so the agent can inspect what defines it and request
|
||||
precise changes from the root agent; RW into the root agent via the
|
||||
`/agents` tree bind.
|
||||
- `config/` — unused. Older hives kept a proposed nix repo here; nothing
|
||||
reads it any more, and you can delete it. The agent's config mount at
|
||||
`/agents/<name>/config` is the applied repo, read-only.
|
||||
- `claude/` — claude OAuth credentials, bind-mounted RW to
|
||||
`/home/<name>/.claude` inside the container.
|
||||
- `state/` — durable notes and `hyperhive-harness.json`. Bind-mounted
|
||||
|
|
@ -487,7 +485,7 @@ boundary a convention rather than a permission.
|
|||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
⚠️ Don't confuse it with the config-repo seeding hive-c0re does at
|
||||
spawn (`lifecycle::setup_proposed`): that writes the agent's initial
|
||||
spawn (`lifecycle::setup_applied`): that writes the agent's initial
|
||||
config repo as **hive-c0re, against the host path**, and `read_only` on a bind
|
||||
constrains writers *inside* a container only. The two are unrelated —
|
||||
conflating them can lead you to reason your way into thinking this
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ forge container:
|
|||
- fetching a merged config commit for a swarm deploy, and
|
||||
`hivectl forge reconcile-config`
|
||||
- reseeding a purged `applied` repo
|
||||
- seeding `proposed` from `agent-configs/<agent>` on first deploy
|
||||
- seeding `applied` from `agent-configs/<agent>` on first deploy
|
||||
- cloning and pulling `/knowledge`
|
||||
|
||||
The hive builds nothing, so no nix process reads the forge as the hive's
|
||||
|
|
|
|||
|
|
@ -402,8 +402,8 @@ sets `EXTRA_NSPAWN_FLAGS` — plus the systemd resource-limits drop-in)
|
|||
into the `Swap` node, then runs `nixos-container update` + stop +
|
||||
start across the `StopForUpdate → Swap → RebuildBookkeeping`
|
||||
brace and the tail `Reconcile` node. `flake.nix` itself lives in the agent's
|
||||
proposed/applied repos and rides along on every fetch (see
|
||||
`docs/agent-lifecycle/config-changes.md::Two repos per agent`).
|
||||
the applied repo and rides along on every fetch (see
|
||||
`docs/agent-lifecycle/config-changes.md::The applied repo per agent`).
|
||||
|
||||
Anything that changes per-container state on the host should be
|
||||
re-applied here so a manual `↻ R3BU1LD` from the dashboard is
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ Cheap — no build slot:
|
|||
| Node | Behavior |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MetaSync` | the rebuild's preamble — rebuild-dir prep, the agent-set refresh (`agent_set::refresh`), and the `internal/meta` commit `Prebuild` and `Swap` expect the swarm's closure to be built from, resolved once and recorded; fails when the repo doesn't pin the agent's current config. Declares no resources |
|
||||
| `Provision` | first-spawn pre-create provisioning — proposed/applied repos, state subvolume, agent-set refresh (`agent_set::refresh`); runs ahead of `Create`. Store-only, no container yet |
|
||||
| `Provision` | first-spawn pre-create provisioning — the applied repo, state subvolume, agent-set refresh (`agent_set::refresh`); runs ahead of `Create`. Store-only, no container yet |
|
||||
| `Reconcile` | idempotent power converge: read `wanted` (below) + observed state; start if `Up` & down (cold-start fallback included), stop if `Offline` & up, else noop |
|
||||
| `Start` | mechanical container start — runtime dir + drop-ins, `start_with_fallback`, MCP listener registration, the manager kick. Fanned out by a `Reconcile` that observed `wanted = Up` and the container down |
|
||||
| `Stop` | mechanical container stop — `nixos-container` kill, MCP listener unregister, the `Killed` manager notify. Fanned out by a `Reconcile` that observed `wanted = Offline` and up |
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ claude-creds badge). One action: `PURG3` (wipes state + applied
|
|||
dirs; `POST /api/purge-tombstone/{name}`).
|
||||
|
||||
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: `Provision`
|
||||
writes every definition-side artifact (state subvolume, proposed + applied
|
||||
repos, meta registration, topology entry) *before* the container exists,
|
||||
writes every definition-side artifact (state subvolume, applied
|
||||
repo, meta registration, topology entry) *before* the container exists,
|
||||
and that artifact set survives `lifecycle::destroy`. An agent
|
||||
part-way through a spawn is byte-identical on disk to a tombstone, and
|
||||
the pane lists both. The pane carries a standing warning to that effect, and
|
||||
|
|
|
|||
Loading…
Reference in a new issue