Commit graph hyperhive/swarm-controller/src/forge.rs
Author SHA1 Message Date
damocles
377dbb57e3 swarm-controller: fix broken intra-doc links (private items, rustdoc lint) 2026-08-19 21:12:06 +02:00
damocles
66c494697f swarm-controller: track agent config-PR status independently of hives 2026-08-19 21:10:59 +02:00
atlas
36810ae598 fix(#3529): create agent config repos in agent-configs, not agents
WIP — compiles per an earlier build, but the verifying build/test run was
cut short by a graceful stop. Re-run gate.sh before pushing.

The controller created every agent config repo, its collaborator entry,
its branch protection and its seeded agent.nix/flake.nix in the agents
org. Config repos live in agent-configs, which is where hive-c0re
reconciles, merges and mirrors them — so a repo created in agents is
invisible to all of those, and nothing errors, because both orgs exist
and both accept a repo.

Root cause was a doc comment asserting something false: AGENTS_ORG
claimed to be the same org hive-c0re uses for its config-repo path. It is
not — hive-c0re's agents org is the namespace repos an AGENT ASKS FOR
land in, and its config repos use agent-configs. The whole flow inherited
the wrong premise from that sentence.

The merge gate survives the move: hive-c0re provisions the operators team
in both orgs, with a comment recording that missing the agent-configs
copy once left every config repo unprotected.
2026-08-19 19:06:46 +02:00
atlas
6dffa74d90 refactor(#3434): drop the agent_repo identity function
mara, PR #3438 review: 'remove the identity function. agent names
are unique and all repos go into agent-configs namespace anyway'. Right --
repo==agent isn't a convention worth a name once every call site can just
say so; call create_repo/add_repo_member/seed_agent_config with &agent
directly.
2026-08-18 20:58:24 +02:00
atlas
4d526b8492 refactor(#3434): the job nodes carry the agent, not the repo too
Every repo in this graph is `agents/<agent>` -- the node payloads were
carrying the same string under two names, and `create_agent` opened with
a `let repo = agent.clone()` that said so out loud.

All four node kinds now carry `agent` alone, and `forge::agent_repo` is
the single home for the naming convention. The identity it returns is the
point: a caller holding an agent name never writes a repo name itself, so
changing the convention later is one edit rather than a search.

`forge::Client`'s methods keep taking a repo, because they are a general
forge client and `add_repo_member(repo, user)` is a real signature -- the
derivation belongs at the call site that knows the two are the same here,
not baked into an API that has no reason to assume it.

`data()`'s four arms are now identical and merged into one or-pattern.
Left as an explicit list rather than a catch-all so a fifth variant fails
to compile here instead of silently rendering as an agent name.
2026-08-18 20:58:24 +02:00
atlas
8b55a8b9fd feat(#3434): agent creation takes the hive it is aimed at
`POST /api/agents` now requires `hive` alongside `name`. It is parsed as
an `Ident` like `name` already was, and then checked against the roster
loaded from `SWARM_CONTROLLER_HIVES` -- a hive that is not in this swarm
is a 400 naming the ones that are, rather than a typo accepted and
forgotten. The roster check is what makes the field worth having; without
it nothing notices until a deploy message is addressed to a hive that
does not exist.

`hive` is an address, not an attribute of the agent: it is where a deploy
message goes over the queue, so nothing writes it into the agent's config
repo. A config naming its own hive would be a second statement of where
the agent lives, free to drift from the queue that actually delivers to
it.

It rides on the `InitAgentConfigRepo` node payload because the graph is
the only thing carrying the operator's choice forward from the API
boundary; seeding does not consume it. The node that routes on it is the
deploy node in #3124.

The refusal is asserted by effect -- the test checks that *nothing was
queued*, not just the status code, since a version that queued the graph
and then complained would satisfy a status-only assertion while still
creating the agent.

This is a breaking change for every existing caller: the swarm-UI create
page posts `{name}` only and needs its hive dropdown to land alongside.
2026-08-18 20:58:24 +02:00
atlas
4573865745 feat(#3255): register the swarm-wide forge hooks against the controller
The endpoint landed inert: nothing pointed at it, so the only way to see
it work was to mint an HMAC by hand. Register the two swarm-wide hooks
at startup so a real forge event produces a journal line.

Registered ALONGSIDE the per-hive hooks, not instead of them. Every hive
keeps receiving and acting on its own deliveries; the controller gets a
copy and logs it. Moving the registration is a later step and has to be:
fan-out swarm->hive does not exist yet, so a hook moved now would point
at a receiver that forwards nowhere, silently on both sides.

Deliberately no stale-hook deletion arm, unlike the two per-hive
registrars this otherwise mirrors: theirs delete hooks matching their own
path with a foreign base, and the hives' hooks are not stale.

The route prefix is what keeps this safe. Both hive-side registrars
delete any hook ending in /webhook/knowledge or /webhook/config-pr with a
different base, so a swarm hook under those paths would be deleted by
every hive on every boot. Serving them under /webhook/forge/ avoids it,
and a test pins it -- there is nothing else that can.

SWARM_CONTROLLER_PUBLIC_URL is set only where the swarm vhost is served,
because a hook whose target_url nothing answers is worse than no hook.
2026-08-18 12:28:09 +02:00
damocles
abf3a9e530 fix(swarm-controller): repair broken rustdoc intra-link in forge.rs 2026-08-17 00:05:12 +02:00
damocles
1d31bb6e80 swarm-controller: CreateRepo/AddRepoMember/InitAgentConfigRepo forge nodes 2026-08-17 00:05:12 +02:00