Reference · reference

playbook.wcl

11 min read · 2026-09-02 · config-weave 0.1.0

playbook.wcl is the WCL document at the root of a playbook directory. It holds one playbook block, which declares the playbook's gathers, its variables, any playbook-local composites, and its plays of steps. The engine appends the system import <weave/playbook.wcl> to the file when it opens the playbook, so you never write an import line. How the pieces fit together is explained in Playbooks, plays and steps; this chapter lists every block and field.

ItemValue
Location<playbook>/playbook.wcl
System import<weave/playbook.wcl>, appended by the engine
Top-level blockplaybook "name" { … }, exactly one per file

Every field marked required below is enforced by the loader, including each block's description. A condition field is a WCL expression that must evaluate to a bool; it is evaluated lazily at run time against the variable scope described under Variable precedence. A property or parameter field shadows an outer variable of the same name, so url = url is a self-reference cycle. Feed a same-named parameter from a variable with a distinct name.

playbook

playbook.wclwcl
playbook "Sample" {
  description = "What this playbook converges"
  version = "1.2.0"

  gather "os" { … }
  vars { … }
  composite "site" { … }
  play "default" { … }
}

The root block. Its child blocks may appear in any order, but a gather result is only visible to vars, conditions and properties, never to other gather params. A playbook may declare any number of plays; check and apply target one play by name.

FieldTypeRequiredMeaning
labelstringyesThe playbook name, reported in every run.
descriptionstringyesOne-line summary, rendered by config-weave docs.
versionstringno, default "0.0.0"A free-form version string, reported with the name.
gatherblock, repeatablenoGatherer invocations. See gather.
varsblock, at most onenoVariable declarations. See vars.
compositeblock, repeatablenoPlaybook-local composites. See composite.
playblock, repeatablenoThe plays. See play.

gather

wcl
gather "os" {
  description = "Operating system facts"
  from = "core.os_info"
  params {
    detail = :full
  }
}

Runs a gatherer declared in a package and binds the returned value to a variable named by the label. All gathers in a playbook run concurrently before any step, and two gathers with the same gatherer and the same canonicalised params run once and share the result. A gather that fails aborts the run before any step executes.

FieldTypeRequiredMeaning
labelstringyesThe variable that receives the gathered value, for example os.family.
descriptionstringnoOne-line summary for the docs.
fromstringyesThe gatherer as package.gatherer.
paramsblock, at most onenoParameters, validated against the gatherer's param declarations. See properties and params.

vars

wcl
vars {
  config_dir = "/etc/sample"
  is_debian = os.family == "linux" && os.distro == "debian"
  db_password = secret("CWENC1.…")
}

A free-form block whose every field declares one variable. A value is any WCL expression. It may reference gatherer results and other variables, and it may wrap a value in secret("…") to keep it encrypted in the file. See Variables and secrets for how values are resolved and how secrets are handled.

FieldTypeRequiredMeaning
any nameexpressionnoDeclares a variable of that name. The block has no fixed fields.

play

wcl
play "default" {
  description = "Base configuration"
  parallel = true

  step "motd" { … }
  container "web" { … }
}

A named group of steps. With parallel = true the scheduler runs steps in dependency order, as many at once as the concurrency classes allow. With parallel = false steps run one at a time in declaration order. Each step and container inside a play may carry its own condition. See Scheduling and concurrency.

FieldTypeRequiredMeaning
labelstringyesThe play name, passed to check and apply.
descriptionstringyesOne-line summary for the docs.
parallelboolno, default truefalse forces strict declaration order.
stepblock, repeatablenoSteps directly under the play. See step.
containerblock, repeatablenoGroups of steps. See container.

container

wcl
container "web" {
  description = "Everything the web tier needs"
  condition = os.family == "linux"

  step "nginx" { … }
  container "tls" { … }
}

Groups steps for organisation and for the generated docs. Containers nest. A condition on a container applies to every step beneath it. A container does not change scheduling: the play's dependency graph is flat across containers, and a requires entry names a step by its own name regardless of the container it sits in.

FieldTypeRequiredMeaning
labelstringyesThe container name, one segment of a step's report path.
descriptionstringyesOne-line summary for the docs.
conditionbool expressionnoWhen false, every child step reports *skipped*.
stepblock, repeatablenoChild steps.
containerblock, repeatablenoNested containers.

step

wcl
step "nginx" {
  description = "Install nginx"
  resource = "linux_apt.package"
  condition = is_debian
  requires = ["update_cache"]
  concurrency = "exclusive"
  properties {
    name = "nginx"
    ensure = :present
  }
}

One unit of work. A step names a resource or a composite and supplies its properties. The engine runs the resource's script through the check, apply and re-check lifecycle described in The convergence contract. The resource field takes a package-qualified name (pkg.resource or pkg.composite) or the bare name of a composite declared in this playbook.

FieldTypeRequiredMeaning
labelstringyesThe step name. Unique within the play; the target of requires.
descriptionstringyesOne-line summary, shown in reports and docs.
resourcestringyesThe resource or composite to run.
conditionbool expressionnoWhen false the step reports *skipped* and its dependents still run.
requireslist of stringsnoStep names that must finish first. Ordering only, not a success demand: an errored or unrun dependency blocks its dependents, a skipped one does not.
concurrencystringnoparallel, exclusive or global. May tighten the resource's declared class, never loosen it; a looser value is a validation error. See Concurrency classes.
propertiesblock, at most onenoThe resource's parameters. See below.

properties and params

wcl
properties {
  path = "/etc/motd"
  content = $"Welcome to ${os.hostname}"
  mode = :strict
  max_age = 30min
}

Both are free-form maps. A step's properties block is validated against the resource's param declarations; a gather's params block against the gatherer's. Validation applies declared defaults, rejects unknown names, checks required names are present, and checks each value against its coarse type. A symbol parameter must be written as :name; the quoted spelling is an error. A duration parameter is written as a bare WCL unit literal (30min, 4h), never quoted. A map parameter is written as a map literal (env = { KEY: "value" }), never as a nested block; a nested block inside properties or params is a validation error. Values may be any WCL expression over the variable scope, and are evaluated lazily when the step is planned.

FieldTypeRequiredMeaning
any declared nameper the param typeper the paramOne parameter value. Names not declared by the resource or gatherer are rejected.

composite

wcl
composite "site" {
  description = "A static site: directory plus index page"
  arg "root" {
    description = "Directory that holds the site"
    type = "string"
    required = true
  }
  arg "body" {
    description = "Contents of index.html"
    type = "string"
    default = "<h1>hello</h1>"
  }

  step "dir" {
    description = "Site directory"
    resource = "core.directory"
    properties { path = args.root }
  }
  step "index" {
    description = "Index page"
    resource = "core.file"
    requires = ["dir"]
    properties {
      path = $"${args.root}/index.html"
      content = args.body
    }
  }
}

A named, parameterised block of steps, invoked from a step exactly like a resource. Declared in a playbook it is local to that playbook and referenced by bare name (resource = "site"). The same block in a package.wcl is shared and referenced qualified. The loader expands each invocation statically into a container of ordinary steps, so reports show the inner steps under a path of container/…/invocation/inner. A body sees only its own arguments, never gatherer results, vars or --var overrides; a fact the body needs is passed in as a property. Inside the body a requires entry reaches only sibling steps of the same invocation, and from the playbook a requires entry naming the invocation waits for every step it expanded into. Nesting is capped at eight levels and a cycle of invocations is rejected by name.

FieldTypeRequiredMeaning
labelstringyesThe composite name. Shares a namespace with resources.
descriptionstringyesOne-line summary for the docs.
argblock, repeatablenoDeclared arguments. See arg.
stepblock, repeatablenoThe body. Each step has the fields of a playbook step, and an invocation's own concurrency tightens every step of the body.

arg

wcl
arg "ensure" {
  description = "Whether the site should exist"
  type = "symbol"
  default = :present
  symbol "present" { description = "Create or update the site" }
  symbol "absent"  { description = "Remove the site" }
}

One declared argument of a composite. It has the shape of a resource param, under its own block kind so the body can read the value as args.name. Each argument binds twice inside the body: bare (ensure) and under the args map (args.ensure). Prefer args.: a property field shadows a bare outer variable of the same name, so properties { path = path } is a self-reference cycle while properties { path = args.path } always works. A symbol argument may enumerate its legal values with symbol child blocks; declaring any closes the set.

FieldTypeRequiredMeaning
labelstringyesThe argument name.
descriptionstringyesOne-line summary for the docs.
typestringyesOne of string, int, float, bool, list, map, symbol, duration.
requiredboolno, default falseWhether an invocation must supply the argument.
defaultvalue of typenoUsed when the invocation omits the argument.
symbolblock, repeatablenoFor type = "symbol" only: one legal value, with a required description.

Variable precedence

console
config-weave apply ./my-playbook baseline --var-file ./site.wcl --var count=3

Each source of a variable's value overrides the ones above it in this table.

SourcePrecedenceNotes
vars declarationlowestExpressions may reference gatherer results and other variables.
gatherer resultsecondBound under the gather label. All gathers run concurrently before steps.
--var-file file.wclthirdA flat name = value file. Each expression evaluates standalone and cannot reference other variables.
--var KEY=VALUEhighestRepeatable. VALUE parses as a WCL expression when it can, so --var count=3 is an int; otherwise it is a plain string.

Gather params evaluate before variables resolve. They may reference --var and --var-file overrides, but not gatherer results or any variable that depends on them. Conditions and properties evaluate lazily at run time against the full scope. See Variables and secrets for the model behind these rules.