Lineup manifests
Work with Lineup's TOML manifests while preserving the surrounding project's conventions. Prefer the smallest manifest structure that expresses the requested workflow.
Follow the workflow
- Locate
LM.local.toml or LM.toml and adjacent Lineup manifests. Lineup selects LM.local.toml automatically when it exists; otherwise it uses LM.toml.
- Read the complete target manifest and any local manifests referenced through
[use], run-taskline, run-taskset, or run-lineup. Inspect nearby manifests and module files for project conventions.
- Inventory reusable modules before writing tasks. Check existing
[use] imports, project-local TOML modules, and Lineup's installed modules. Read references/modules.md for built-in contracts and module selection.
- Read references/manifest.md before creating a manifest, introducing an unfamiliar section/task/engine, or diagnosing schema behavior.
- Model execution explicitly:
- define execution targets under
[workers];
- define reusable sequential steps in
[[tasklines.NAME]] before the [taskset.NAME] entries that execute them;
- put taskset execution wiring after taskline definitions and order it with
requires;
- keep shared inputs in
[vars] and task-local inputs in vars.*;
- import reusable variables or tasklines with
[use].
- Prefer a module taskline whenever its contract covers the operation. Import it once under
[use], call it with run, and pass its declared variables. Use direct run-taskline when importing the whole module would be unnecessary or would cause name collisions.
- Write a project-local module when a multi-step operation is reusable across tasklines or manifests. Use a pure
shell or exec task only for a genuinely one-off operation or when no suitable module exists.
- Preserve valid TOML types. Do not stringify arrays, objects, numbers, or booleans unless template rendering requires a string. Quote dotted or otherwise special TOML keys.
- Treat template-bearing strings as Tera expressions. Shell-escape variable values with
| quote or | q when interpolating them into shell commands.
- Validate safely. Parse TOML locally when a TOML parser is available, then run Lineup only when execution is authorized and its workers/resources are safe to create. Prefer a debug engine or an isolated test manifest when execution would mutate systems.
- Report validation performed, runtime assumptions, and anything that was not executed.
When iterating on only part of a workflow, read references/execution-control.md before choosing task filters, preserving workers, or reusing resume history. Resume history is structural and can become stale after manifest edits.
Apply design rules
- Search modules first. Prefer
apt-get.install, systemctl.enable, useradd, sed, wait, or another existing module taskline over spelling out the equivalent command.
- Define
host as the string exception, [workers.NAME] engine = "host". Define every other worker engine as a nested table such as [workers.NAME.engine.vml], [workers.NAME.engine.podman], or [workers.NAME.engine.ssh].
- Use
shell.cmd directly for shell expressions, pipelines, redirections, substitutions, compound commands, and tests. Do not wrap these in exec.args = ["sh", "-c", ...] or exec.args = ["sh", "-eu", "-c", ...].
- Use
exec.args for a remaining one-off command only when every argument can be passed literally without shell parsing.
- Split long shell scripts into focused taskline entries when the steps can run independently. This improves failure context and resume granularity. Keep commands together when they must share shell state or form one transactional operation.
- Prefer Lineup command checks over shell assertion plumbing: commands already fail on an unexpected return code; use
success-codes for accepted nonzero codes and success-matches or failure-matches to test output with regexes. Keep output checks as dedicated taskline entries; use test.commands only for a compact group of return-code checks. See references/manifest.md.
- Prefer concise Rust-regex shorthands such as
\s, \d, and \w over POSIX classes such as [[:space:]], [[:digit:]], and [[:word:]] when their semantics fit.
- Shape command output with Lineup's
result controls and pass the typed result to the next taskline entry. Use result-fs-var when the value must survive beyond the current taskline context. See references/manifest.md.
- Set
shell.stdout.print = true only when output should be user-visible; Lineup otherwise logs command output.
- Use
condition for worker-side preconditions and if for rendered boolean conditions when supported by the existing manifest/version.
- Use
ensure.vars at taskline boundaries to document required inputs and their types.
- Use
clean-vars, export-vars, and break deliberately when designing module boundaries or early-success paths. Check version-specific retry semantics before setting try.attempts; see references/manifest.md.
- Use
items for genuine repetition. Set parallel = false when ordering or shared state makes concurrent items unsafe.
- Prefer
items, table, table-by-item, and table-by-name over duplicated tasks or workers when configuration is data-driven. Read references/manifest.md before using command-generated data.
- Limit taskset execution with
workers regexes. Remember that taskset tasks otherwise run on all workers.
- Use
when = "before" or when = "after" for global setup/teardown phases instead of large dependency lists.
- Use
try only for plausibly transient failures, with bounded attempts and cleanup when partial state can remain.
- Resolve relative paths from the directory containing the manifest. Use
{{ manifest_dir }} when a host-side path must be explicit.
- Avoid embedding secrets in manifests. Pass values through the environment/project mechanism or
--extra-vars when appropriate, and avoid printing them.
- Do not invent fields. Check the reference and the installed
lineup --help or version when repository usage conflicts with upstream examples.
Reuse modules
Prefer importing related tasklines once:
[use]
tasklines = ["apt-get", "systemctl"]
[[tasklines.setup]]
run = "apt-get.install"
vars.packages = ["nginx"]
[[tasklines.setup]]
run = "systemctl.enable"
vars.services = ["nginx"]
Use a taskline directly when only one operation is needed:
run-taskline = { module = "sed", taskline = "" }
vars.file = "/etc/example.conf"
vars.expression = "s/^enabled=.*/enabled=true/"
Do not duplicate a module's internal shell implementation in the calling manifest. Pass its public variables and let the module own validation, quoting, defaults, and sequencing.
Prefer the native shell task when the command is already shell code:
shell.cmd = '[ "$(id -un)" = builder ]'
Do not express the same operation through a shell executable:
# Avoid
exec.args = ["sh", "-eu", "-c", "[ \"$(id -un)\" = builder ]"]
Lineup's shell task already runs a shell command and checks failure by default. Add an explicit shell invocation only when a particular non-default shell or shell-specific option is itself required by the workflow.
Use the CLI conservatively
- Initialize a basic manifest with
lineup init; it writes LM.toml by default.
- Select another manifest with
lineup --manifest PATH.
- Override values with repeated
--extra-vars NAME=VALUE arguments.
- Use task filters, cleanup controls, and resume only after reading references/execution-control.md. These are CLI execution controls, not manifest schema.
- Consult the installed
lineup --help before applying the reference to another version.
Review changes
Check all of the following before handing off:
- every
requires target, taskline name, worker selector, module, and relative path resolves;
- every module call uses an exported taskline and supplies its required variables with compatible types;
- each task has exactly one intended task type;
- shared workers using the same VM/container coordinate
name and setup correctly;
- task ordering matches data and resource dependencies;
- cleanup behavior will not delete state the workflow expects to retain;
- template expressions receive variables of the expected type;
- shell interpolation uses quoting and does not expose secrets;
- TOML remains parseable and names containing dots or template syntax are quoted.
1---2name: lineup3description: Create, inspect, edit, and troubleshoot Lineup orchestration manifests (`LM.toml` and `LM.local.toml`) for tasks running on host, SSH, Docker, Podman, Incus, or VML workers, with a strong preference for reusable Lineup modules over ad hoc shell commands. Use for Lineup manifest structure, built-in or project modules, workers, tasklines, tasksets and dependencies, variables and Tera templates, networks, storages, file transfers, retries, conditional execution, nested manifests, cleanup, and resume behavior.4---56# Lineup manifests78Work with Lineup's TOML manifests while preserving the surrounding project's conventions. Prefer the smallest manifest structure that expresses the requested workflow.910## Follow the workflow11121. Locate `LM.local.toml` or `LM.toml` and adjacent Lineup manifests. Lineup selects `LM.local.toml` automatically when it exists; otherwise it uses `LM.toml`.132. Read the complete target manifest and any local manifests referenced through `[use]`, `run-taskline`, `run-taskset`, or `run-lineup`. Inspect nearby manifests and module files for project conventions.143. Inventory reusable modules before writing tasks. Check existing `[use]` imports, project-local TOML modules, and Lineup's installed modules. Read [references/modules.md](references/modules.md) for built-in contracts and module selection.154. Read [references/manifest.md](references/manifest.md) before creating a manifest, introducing an unfamiliar section/task/engine, or diagnosing schema behavior.165. Model execution explicitly:17 - define execution targets under `[workers]`;18 - define reusable sequential steps in `[[tasklines.NAME]]` before the `[taskset.NAME]` entries that execute them;19 - put taskset execution wiring after taskline definitions and order it with `requires`;20 - keep shared inputs in `[vars]` and task-local inputs in `vars.*`;21 - import reusable variables or tasklines with `[use]`.226. Prefer a module taskline whenever its contract covers the operation. Import it once under `[use]`, call it with `run`, and pass its declared variables. Use direct `run-taskline` when importing the whole module would be unnecessary or would cause name collisions.237. Write a project-local module when a multi-step operation is reusable across tasklines or manifests. Use a pure `shell` or `exec` task only for a genuinely one-off operation or when no suitable module exists.248. Preserve valid TOML types. Do not stringify arrays, objects, numbers, or booleans unless template rendering requires a string. Quote dotted or otherwise special TOML keys.259. Treat template-bearing strings as Tera expressions. Shell-escape variable values with `| quote` or `| q` when interpolating them into shell commands.2610. Validate safely. Parse TOML locally when a TOML parser is available, then run Lineup only when execution is authorized and its workers/resources are safe to create. Prefer a debug engine or an isolated test manifest when execution would mutate systems.2711. Report validation performed, runtime assumptions, and anything that was not executed.2829When iterating on only part of a workflow, read [references/execution-control.md](references/execution-control.md) before choosing task filters, preserving workers, or reusing resume history. Resume history is structural and can become stale after manifest edits.3031## Apply design rules3233- Search modules first. Prefer `apt-get.install`, `systemctl.enable`, `useradd`, `sed`, `wait`, or another existing module taskline over spelling out the equivalent command.34- Define `host` as the string exception, `[workers.NAME] engine = "host"`. Define every other worker engine as a nested table such as `[workers.NAME.engine.vml]`, `[workers.NAME.engine.podman]`, or `[workers.NAME.engine.ssh]`.35- Use `shell.cmd` directly for shell expressions, pipelines, redirections, substitutions, compound commands, and tests. Do not wrap these in `exec.args = ["sh", "-c", ...]` or `exec.args = ["sh", "-eu", "-c", ...]`.36- Use `exec.args` for a remaining one-off command only when every argument can be passed literally without shell parsing.37- Split long shell scripts into focused taskline entries when the steps can run independently. This improves failure context and resume granularity. Keep commands together when they must share shell state or form one transactional operation.38- Prefer Lineup command checks over shell assertion plumbing: commands already fail on an unexpected return code; use `success-codes` for accepted nonzero codes and `success-matches` or `failure-matches` to test output with regexes. Keep output checks as dedicated taskline entries; use `test.commands` only for a compact group of return-code checks. See [references/manifest.md](references/manifest.md#validate-commands-natively).39- Prefer concise Rust-regex shorthands such as `\s`, `\d`, and `\w` over POSIX classes such as `[[:space:]]`, `[[:digit:]]`, and `[[:word:]]` when their semantics fit.40- Shape command output with Lineup's `result` controls and pass the typed `result` to the next taskline entry. Use `result-fs-var` when the value must survive beyond the current taskline context. See [references/manifest.md](references/manifest.md#capture-and-persist-results).41- Set `shell.stdout.print = true` only when output should be user-visible; Lineup otherwise logs command output.42- Use `condition` for worker-side preconditions and `if` for rendered boolean conditions when supported by the existing manifest/version.43- Use `ensure.vars` at taskline boundaries to document required inputs and their types.44- Use `clean-vars`, `export-vars`, and `break` deliberately when designing module boundaries or early-success paths. Check version-specific retry semantics before setting `try.attempts`; see [references/manifest.md](references/manifest.md#control-context-flow-and-retries).45- Use `items` for genuine repetition. Set `parallel = false` when ordering or shared state makes concurrent items unsafe.46- Prefer `items`, `table`, `table-by-item`, and `table-by-name` over duplicated tasks or workers when configuration is data-driven. Read [references/manifest.md](references/manifest.md#drive-tasks-and-workers-from-data) before using command-generated data.47- Limit taskset execution with `workers` regexes. Remember that taskset tasks otherwise run on all workers.48- Use `when = "before"` or `when = "after"` for global setup/teardown phases instead of large dependency lists.49- Use `try` only for plausibly transient failures, with bounded attempts and cleanup when partial state can remain.50- Resolve relative paths from the directory containing the manifest. Use `{{ manifest_dir }}` when a host-side path must be explicit.51- Avoid embedding secrets in manifests. Pass values through the environment/project mechanism or `--extra-vars` when appropriate, and avoid printing them.52- Do not invent fields. Check the reference and the installed `lineup --help` or version when repository usage conflicts with upstream examples.5354## Reuse modules5556Prefer importing related tasklines once:5758```toml59[use]60tasklines = ["apt-get", "systemctl"]6162[[tasklines.setup]]63run = "apt-get.install"64vars.packages = ["nginx"]6566[[tasklines.setup]]67run = "systemctl.enable"68vars.services = ["nginx"]69```7071Use a taskline directly when only one operation is needed:7273```toml74run-taskline = { module = "sed", taskline = "" }75vars.file = "/etc/example.conf"76vars.expression = "s/^enabled=.*/enabled=true/"77```7879Do not duplicate a module's internal shell implementation in the calling manifest. Pass its public variables and let the module own validation, quoting, defaults, and sequencing.8081Prefer the native shell task when the command is already shell code:8283```toml84shell.cmd = '[ "$(id -un)" = builder ]'85```8687Do not express the same operation through a shell executable:8889```toml90# Avoid91exec.args = ["sh", "-eu", "-c", "[ \"$(id -un)\" = builder ]"]92```9394Lineup's shell task already runs a shell command and checks failure by default. Add an explicit shell invocation only when a particular non-default shell or shell-specific option is itself required by the workflow.9596## Use the CLI conservatively9798- Initialize a basic manifest with `lineup init`; it writes `LM.toml` by default.99- Select another manifest with `lineup --manifest PATH`.100- Override values with repeated `--extra-vars NAME=VALUE` arguments.101- Use task filters, cleanup controls, and resume only after reading [references/execution-control.md](references/execution-control.md). These are CLI execution controls, not manifest schema.102- Consult the installed `lineup --help` before applying the reference to another version.103104## Review changes105106Check all of the following before handing off:107108- every `requires` target, taskline name, worker selector, module, and relative path resolves;109- every module call uses an exported taskline and supplies its required variables with compatible types;110- each task has exactly one intended task type;111- shared workers using the same VM/container coordinate `name` and `setup` correctly;112- task ordering matches data and resource dependencies;113- cleanup behavior will not delete state the workflow expects to retain;114- template expressions receive variables of the expected type;115- shell interpolation uses quoting and does not expose secrets;116- TOML remains parseable and names containing dots or template syntax are quoted.