These patterns assume a portable pack, so they execute in-process and depend
only on hubuum-cli. Copy the complete, compile-checked versions from
examples/hubuum-recipes.
A command contract is the CLI's description of how a workflow may call one
of its built-in commands. It names the keys accepted by the step's with
object and describes each key's value type, whether it is required, and whether
it accepts one value or a repeated group. The contract also reports operational
properties such as whether the command needs authentication or may change data.
contract --list lists the built-ins that a run step may call. Pass one of
those command paths to inspect its contract:
hubuum-cli extension contract --list
hubuum-cli extension contract object listThe second command reports that where has repeated_fixed cardinality with
groups of three. Bind one clause as a flat array:
Use the source that owns the value:
"with": {
"from_user": { "input": "class" },
"from_site": { "config": "hosts_class" },
"from_step": { "step": "classes" },
"one_field": { "step": "classes", "select": ".[0].name" },
"plain_text": "Hosts",
"object_data": { "literal": { "site": "oslo" } }
}The keys on the left must exist on the called command or workflow. The example
uses descriptive placeholders; a real run uses IDs from extension contract.
Declare a workflow without a corresponding command, then call it:
{
"id": "hosts",
"kind": "call",
"call": "list_one",
"with": { "class": "Hosts" }
}Calls stay in the current pack. Input names, types, and repeatability are checked statically. The compiler rejects missing targets and call cycles.
The target workflow declares an input matching as:
{
"id": "objects_by_class",
"kind": "for_each",
"items": { "input": "classes" },
"as": "class",
"call": "list_one",
"max_items": 10,
"when": ".input.enabled"
}classes must resolve to an array. Runtime length must be no more than both
max_items and the host limit. Results preserve input order.
Use let for a bounded transformation and assert for an invariant:
[
{
"id": "names",
"kind": "let",
"expr": ".steps.hosts | map(.name)"
},
{
"id": "has_hosts",
"kind": "assert",
"condition": "(.steps.names | length) > 0",
"message": "No Hosts were found"
}
]An assertion condition and a when expression must yield exactly one Boolean.
The workflow input:
"classes": {
"type": "string",
"repeatable": true,
"default": ["Hosts", "Jacks"]
}must match its public command option:
"classes": {
"kind": "string",
"long": "class",
"repeatable": true,
"help": "Class to visit; may be repeated"
}Users can then pass --class Hosts --class Jacks.
First inspect the built-in target:
hubuum-cli extension contract relation object createAny workflow whose expanded calls may mutate data declares the capability:
"capabilities": ["mutate"]Every calling workflow must make this acknowledgement. The compiler propagates
mutation and replay safety through call and for_each. This is an effects
declaration, not a transaction: completed writes are not rolled back after a
later failure.
Run:
hubuum-cli extension explain examples/hubuum-recipes --workflow tour \
--output jsonThe workflow entry at .plan.workflows[0] contains these sections (irrelevant
fields are omitted here):
{
"name": "tour",
"effects": "read_only",
"requires_authentication": true,
"reauthentication_retry": "safe",
"call_depth": 2,
"worst_case_operations": 38,
"output": {
"shape": "detail",
"type": "json"
},
"steps": [
{
"id": "configured",
"kind": "run",
"run": "object list",
"with": {
"class": { "config": "fallback_class" }
}
}
]
}effectsis expanded across nested calls, not copied from the outer step.requires_authenticationsays execution needs a configured server session; validate and explain themselves remain offline.reauthentication_retrydescribes whether the whole exposed command can be replayed safely after renewing a session.call_depthandworst_case_operationsare compiler results checked against the adjacentlimitsobject.stepsis the stableWorkflowPlan, with binding sources made explicit.
Use --workflow to keep a large pack readable. Without it, explain returns
all public and private workflow plans.
Use this order:
- Let the editor schema catch misspelled fields and wrong JSON shapes.
- Run
extension contractfor an unknownrunbinding or wrong cardinality. - Run
extension validatefor the complete cross-reference and type error. - Run
extension explain --workflow NAMEto verify effects, conditions, normalized bindings, call expansion, and limits. - For installed discovery failures, run
extension doctor --output json.
Forward step references, cross-pack calls, cycles, undeclared mutation, unbounded iteration, wrong output shapes, and incompatible command interfaces are all rejected before a portable command is registered.
{ "id": "servers", "kind": "run", "run": ["object", "list"], "with": { "class": "Hosts", "where": ["name", "contains", "server"], "all": true } }