Actions
An action is a function addressed to an agent and executed on its machine. The verb is a Call — a named body with a bound argument; the same value serves one agent and a whole set.
Declaring inline
Name and body live right where they are needed; arguments travel only through the binding — explicit and serializable:
report, err := pipelineactivity.Activity(ctx, bareAgent,
pipelineactivity.ActivityFn(
"run-work",
func(ctx context.Context, work string) (string, error) {
out, err := machine.Command(ctx, "/bin/sh", "-c", work).CombinedOutput()
return string(out), err
},
params.Work,
),
)
The body executes in the machine container; machine.Command chroots
into the host's filesystem — the executor image itself has no shell.
Declare actions unconditionally (not behind branches on runtime
values): the recording pass sees the zero-value path.
Fan-out
"Run it on all who are marked": select agents by labels, then one call on every agent in parallel. Results align with targets; failures are joined into one error naming each agent:
edges, err := pipeline.SelectAgents(ctx,
pipeline.WithLabels(map[string]string{"role": "edge"}))
...
installReports, err := pipelineactivity.ActivityAll(ctx, edges, dockerlib.Install())
A library verb is the same Call value — dockerlib.Install() works
in Activity and ActivityAll alike.
Guarantees
Every action has an execution guarantee:
- At-least-once (default): converging work, retried by policy — write the body idempotent.
- At-most-once: one-shot work, never retried; an undeterminable
outcome surfaces as
ErrUnknown— never as a second execution.
pipelineactivity.Activity(ctx, agent, call,
pipelineactivity.WithGuarantee(pipelineactivity.AtMostOnce),
pipelineactivity.WithTimeout(5*time.Minute), // bounds one execution (default 10m)
pipelineactivity.WithHeartbeat(30*time.Second), // "still running" vs "died"
)