Concept
Template runtime contract
The `linkar_template.yaml` contract, `run.command` versus `script.sh`, and what render and run do.
Linkar templates are small runtime contracts, not mini workflow languages.
The canonical template file is linkar_template.yaml. Linkar still accepts template.yaml for
backward compatibility, but new templates should use the canonical name.
Template shape
The smallest useful template is usually:
id: simple_echo
version: 0.1.0
description: Write one greeting file.
params:
name:
type: str
required: true
outputs:
greeting_file:
path: greeting.txt
run:
mode: direct
command: >-
printf 'hello %s\n' "${param:name}" > "${LINKAR_RESULTS_DIR}/greeting.txt"
Top-level fields:
idversiondescriptionparamsoutputscleanuptoolsrun
Parameter types
Linkar supports:
strintfloatboolpathlist[path]
Use explicit defaults in the schema whenever possible. That keeps wrapper logic small and makes the CLI help clearer.
Declared outputs
Outputs are how Linkar records what a template produced.
Common patterns:
outputs:
results_dir: {}
report_html:
path: reports/report.html
fastq_files:
glob: output/**/*.fastq.gz
Rules:
results_dir: {}means the wholeresults/directorypathis resolved relative toresults/globis also resolved relative toresults/- only existing outputs are recorded
Cleanup rules
Templates can also declare disposable runtime artifacts:
cleanup:
- path: .pixi
type: dir
- path: work
type: dir
- path: .nextflow
type: dir
- glob: ".nextflow.log*"
type: file
These rules are consumed by:
linkar clean .
clean can be run from a project root or from a rendered template directory.
When run from a project root, Linkar reads project.yaml, finds the recorded
template directories, and applies each template’s rules to its own directory.
When run from a rendered template directory, only that directory is cleaned.
When the same template id is available from a current configured pack, Linkar
uses the latest cleanup rules from that pack instead of keeping cleanup pinned
to the version that originally rendered the workspace.
The default terminal flow previews the matching folders and files before asking
for confirmation.
Rules:
pathandglobare resolved relative to the rendered template directory- each rule must declare exactly one of
pathorglob typecan bedir,file, orany- absolute paths,
.., and rules that target the template root are rejected - declared outputs and scientific results should not be cleanup targets
Tool requirements
Templates can declare external commands that must exist before execution:
tools:
required:
- pixi
required_any:
- [bcl-convert, bcl_convert]
This is a preflight check. It is not an environment manager. When Linkar itself is running inside
a Pixi environment, it also honors Pixi’s PIXI_EXE variable and exposes that executable to
template subprocesses. Nested template-local pixi run commands therefore remain available even
when Pixi did not add its own binary directory to PATH.
run.command versus script.sh
Use run.command for a thin wrapper around one real command.
Use script.sh when the template needs real local shell logic:
- branching
- temp files
- generated configs
- multi-step local orchestration
- traps and cleanup
run.py is also a good option once shell stops being clearer.
Explicit parameter placeholders
The preferred form in run.command is:
"${param:bcl_dir}"
"${param:samplesheet}"
${param:run_name:+--run-name "${param:run_name}"}
This is clearer than relying on the older implicit shell convention where bcl_dir became
BCL_DIR.
Linkar still supports the older form because existing templates use it, but new templates should
prefer ${param:...}.
What linkar render does
linkar render ... stages a standalone runnable artifact and stops there.
If the chosen output directory already contains files, render stops with a clear error; choose a
different --outdir or remove the existing directory before rendering.
Bare template names such as export or nfcore_methylseq resolve through configured packs first.
If you want to execute a template from the current filesystem path explicitly, pass ./template_dir
or an absolute path instead of a bare name.
Render behavior:
- writes one final
run.sh - for
run.entry: script.sh, wraps the source script in the renderedrun.shartifact - resolves parameters into the rendered script
- localizes bound file parameters into the rendered directory when needed
- writes metadata under
.linkar/ - does not execute the template
- when inside a project, records the rendered bundle in
project.yamlwithstate: rendered
In a project, render defaults to the visible project path such as ./demultiplex, not to
.linkar/runs/....
What linkar run does
linkar run ... always executes.
Run behavior:
- for
run.mode: direct, stages the run under.linkar/runs/<instance_id>/ - for
run.mode: renderinside a project, executes directly in the visible project directory such as./export - executes the template
- collects declared outputs
- writes project-central
.linkar/meta/<instance_id>.jsonand.linkar/runtime/<instance_id>.jsonrecords, or portable.linkar/meta.jsonand.linkar/runtime.jsonrecords outside a project - appends a run record to
project.yamlwithstate: completedorstate: failed - updates the stable project alias such as
./fastqcwhen a separate history run directory exists
For render-mode templates in a project:
linkar render TEMPLATEcreates the visible bundle when its target is empty or absent- a second
linkar render TEMPLATEcurrently stops rather than overwriting a non-empty bundle linkar run TEMPLATEruns the current visible bundle if it already existslinkar run TEMPLATE --refreshrerenders first, then runs- if no visible bundle exists yet,
linkar run TEMPLATEmaterializes it once and then executes it
That split is intentional:
rendercreates a handoff artifact and records that current draft asrenderedruncreates immutable executed history for direct-run templates and records execution state for both direct and render-mode templates
Manual execution after render
If you manually execute a rendered run.sh, Linkar is not involved in the execution itself.
You can collect outputs afterward with:
linkar collect /path/to/rendered_dir
That updates the run’s registered metadata JSON and, when the rendered artifact belongs to a project, also updates
its recorded outputs in project.yaml.