Watch
0
0
Fork
You've already forked hyperhive
0

an archetype carries its own notification-router flows for its agents

ArchetypeMeta gets flows, checked when the controller stores the record: every todo must go to the new members recipient, which the router expands to the agents of the archetype. The operator's flows document refuses members. The router reads the archetype records and each roster agent's archetype key, and its NATS grant gains read on both buckets.
This commit is contained in:
atlas 2026-11-11 04:36:25 +01:00 • committed by mara
commit 1efbb72fdd

View file

@ -85,7 +85,7 @@ each one runs, so one event can reach more than one leaf. A flow must be a tree:
one source, one parent for every other node, every node reachable, no children
under a `todo` or a `request_reviewer`. The most is 64 nodes, 16
levels deep, and 100 custom flows. A `todo` node sends to the agents an event
field holds (`assignees_added`, `todo_holders`, …) or to one named agent.
field holds (`assignees_added`, `todo_holders`, …) or to one named agent. An archetype's flows also send to its members ([below](#archetype-flows)).
The router ships the eight flows above, each a source → match on `action` →
todo (`conflict` and `conflict-cleared` also match `mergeability_changed`), and `head-classifier`: source `pull_request` and `pull_request_sync` →
@ -274,6 +274,21 @@ Before the router has read any document, "before" means the built-ins, so a bad
document never leaves it with no flows. The controller refuses to overwrite a
document of a version it doesn't know.
**Archetype flows.** An archetype carries flows of its own in its record's
`meta.flows` ([operator page](../../2-handbook/swarm/archetypes.md#notification-flows)).
Each is an ordinary flow with one more recipient, `Recipients::Members` (`"members"`
on the wire): every agent whose `agent-archetype` key names the archetype the
flow came from. The controller checks each one with `Flow::validate_archetype` when it stores the record: the check
of any flow, except that every `todo` goes to `members` and no node requests a
review. The operator's document refuses `members` (`Flow::validate`), because it has
no archetype. The router reads the `agent-archetypes` record and the
`agent-archetype` key of each agent in its roster at most every 10 seconds, when an event arrives, and runs
each archetype's valid, enabled flows after the built-ins and the document's flows, for the
archetype's agents. The router logs a flow it can't use and leaves it out. A read that fails keeps the archetype flows it had.
Each runs under the id `<archetype>:<id>`, so its todos' source is
`router:<archetype>:<id>`. A recipient not in the roster gets nothing, the same as a named agent that doesn't exist.
A todo key is the dedupe key as for any flow: the same key from the operator's document replaces the archetype's todo.
**Deploying in any order.** The controller, the router and the UI can be on
different hosts. A router that predates `request_reviewer` reads a document
holding such a node (a custom flow with one) as one it can't read and keeps the
@ -283,6 +298,7 @@ as before. A router that has `request_reviewer`, with a
controller that predates it, gets no answer to the request and logs that. A router that predates the `conflict` flows ignores the `pull_request_mergeability` events a newer controller sends, and gives no conflict todos; a controller that predates them sends none, so the new flows never run. A controller that predates this leaves the key absent, and the
router runs the built-ins as before. A router that predates this ignores the
document, so the page and the API show a state the router isn't yet running.
A router that predates archetype flows ignores `meta.flows`, and a controller that predates them drops the field when it stores the record, so the agents of an archetype get no router todos from it until both are new. Deploy `swarm-nats-auth` before the router: without its read grant on the two archetype buckets, each read times out, the router logs it and runs without archetype flows, and the event that triggered the read waits for the timeout.
## Deliveries and dry runs
@ -355,11 +371,11 @@ route: the page, if newer, shows the failure.
## Credentials and grants
| Principal | Grant |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `swarm-notify-router`, in its container | store: `read` on `swarm/services/<router client id>/oidc/client`. Queue: its own consumer on `SWARM_EVENTS` (create, pull, ack) and `STREAM.MSG.GET` on it for the dry run, publish on `$SWARM.router.todo.*` and on `$SWARM.router.sink.request-reviewer`, create `ROUTER_TODOS`, create `NOTIFY_ROUTER_LOG`, publish its one subject and read it by sequence, its own `notify-router-state` bucket, read on the `notify-flows` bucket, `STREAM.INFO` on the agent-status bucket, which lists the agents, and subscribe to the two `$SWARM.router.api.*` request subjects and answer each once, for 10 seconds |
| swarm-controller | publish on `$SWARM.events.forge.*` and on the two `$SWARM.router.api.*` request subjects; subscribe to `$SWARM.router.sink.request-reviewer` and answer each request once, for 30 seconds; creates and writes the `notify-flows` bucket (every bucket, by its `$KV.*.>` grant) |
| an agent, under its own credential | create its own consumer `todo-<agent>` in the form whose filter is `$SWARM.router.todo.<agent>`, pull from it, and ack what it delivered |
| Principal | Grant |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `swarm-notify-router`, in its container | store: `read` on `swarm/services/<router client id>/oidc/client`. Queue: its own consumer on `SWARM_EVENTS` (create, pull, ack) and `STREAM.MSG.GET` on it for the dry run, publish on `$SWARM.router.todo.*` and on `$SWARM.router.sink.request-reviewer`, create `ROUTER_TODOS`, create `NOTIFY_ROUTER_LOG`, publish its one subject and read it by sequence, its own `notify-router-state` bucket, read on the `notify-flows` bucket, read on the `agent-archetypes` bucket (with `STREAM.INFO`) and the `agent-archetype` bucket, `STREAM.INFO` on the agent-status bucket, which lists the agents, and subscribe to the two `$SWARM.router.api.*` request subjects and answer each once, for 10 seconds |
| swarm-controller | publish on `$SWARM.events.forge.*` and on the two `$SWARM.router.api.*` request subjects; subscribe to `$SWARM.router.sink.request-reviewer` and answer each request once, for 30 seconds; creates and writes the `notify-flows` bucket (every bucket, by its `$KV.*.>` grant) |
| an agent, under its own credential | create its own consumer `todo-<agent>` in the form whose filter is `$SWARM.router.todo.<agent>`, pull from it, and ack what it delivered |
No other client may publish on `$SWARM.router.todo.*`, `$SWARM.router.log`, the two
`$SWARM.router.api.*` subjects, `$SWARM.router.sink.request-reviewer` or