datum-agent 0.10.1

Embeddable Datum job registry and lifecycle supervisor
Documentation
# datum-agent

`datum-agent` is the local operations satellite for Datum. It provides an embeddable job registry
actor for named, supervised, materialized streams, including graceful drain, hard stop, restart with
backoff, lifecycle status snapshots, and a stream-native event feed.

It also provides the Datum Control Protocol (DCP) server/client layer used by the daemon, CLI, and
cluster control plane. DCP is prost over length-prefixed TCP/QUIC frames: QUIC with mTLS
is the remote deployment path, and plaintext TCP is accepted only for loopback local development.
The DCP major version is negotiated during `Hello`; unknown major versions are rejected. Minor
version bumps are additive within the same major, so new request variants such as
`ListClusterJobs` and `ClusterNodeInfo` do not change the major.

`StartJob` uses a registered-factory model:
`StartJob { factory_name, instance_name, params }`. The daemon registers named `JobSpec` factories
at startup; closures and stream blueprints do not cross the wire. Cluster placement uses the same
registered factories; dynamic blueprint upload remains out of scope.

## Cluster-aware agents

`ClusterAgent` starts the local `Agent`, DCP server, `datum-cluster` membership node, and DCP
node-session manager together. Members that are `Up` and advertise the `agent` role maintain
single-owner async DCP sessions to each other. QUIC+mTLS is the remote transport; loopback TCP is
available for tests and local development.

Cluster DCP requests now include WP-C3 placement. `SubmitClusterJob { factory_name,
instance_name, params, placement }` can be sent to any cluster-aware agent; non-coordinators forward
it over their C2 node session to the deterministic placement coordinator. The coordinator is the
oldest `Up` member in the local C1 view, tie-broken by node id, with the same split-brain caveat as
timeout downing: during a partition, both sides can transiently place jobs until v0.11+ adds quorum
or lease fencing.

`PlacementSpec` v1 supports an optional role constraint and either `LeastJobs` or `Pinned(node_id)`.
The coordinator starts the selected registered factory on the target node, stores the assignment in
memory, and replicates the assignment best-effort to peers so the next coordinator can take over.
On takeover it rebuilds the placement table by querying local registries across surviving nodes.
There is no persistent placement store in v0.10.

When a member with active cluster-submitted jobs reaches `Down`, the coordinator starts fresh
placement generations on surviving eligible nodes. This is at-least-once materialization: the downed
node may still be running its old copy until it observes its own downing. Fencing is a named
follow-up. Jobs started directly on one agent without cluster placement metadata are local-only and
are never re-placed.

`ListClusterJobs` returns local and peer registry snapshots with placement metadata, generation, and
history for cluster jobs. `DrainJob`, `StopJob`, and `JobStatus` can be marked cluster-scoped; they
route through the coordinator to the current placement node. `ClusterNodeInfo` returns the local
membership view plus each peer session state.

The `datum-agent` binary enables this wiring with `--cluster` or `DATUM_AGENT_CLUSTER=1`. Its
simple config file accepts a `[cluster]` section for keys such as `node_id`, `roles`, `bind_addr`,
`advertise_addr`, `seeds`, `dcp_transport`, and fan-out/reconnect timeouts; `[dcp]` maps to the
existing DCP listener keys.