Concepts

Pipeline

The high-level pipeline crosses an explicit project boundary:

// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2026 SECORO AG (secoro.uni-bremen.de)
// Author: Vamsi Kalagaturu

digraph pipeline {
  rankdir=LR;
  graph [bgcolor="white", nodesep=0.35, ranksep=0.65, compound=true];
  node [shape=box, style="rounded,filled", fillcolor="white", fontname="sans-serif"];
  edge [fontname="sans-serif"];

  subgraph cluster_dsl {
    label="motion-spec-dsl (authoring frontend)";
    color="#2f81f7";
    penwidth=2;
    style="rounded,filled";
    fillcolor="#eaf3ff";

    source [label=".robmot + .scenex + .fsm"];
    rdf [label="parse + resolve + emit\nJSON-LD RDF dataset"];
    source -> rdf;
  }

  subgraph cluster_motion_spec {
    label="motion-spec (compiler and runtime toolchain)";
    color="#8a63d2";
    penwidth=2;
    style="rounded,filled";
    fillcolor="#f3edff";

    check [label="check\nSHACL validation"];
    ir [label="gen ir\nmotion-spec IR"];
    cpp [label="gen / gen code\ngenerated C++"];
    build [label="build\nreusable executable"];
    run [label="run\nREC + frame log"];
    replay [label="replay\nsummary / decoded frames"];

    ir -> cpp -> build;
    build -> run [label="1..n"];
    run -> replay;
  }

  rdf -> ir [ltail=cluster_dsl, lhead=cluster_motion_spec];
  rdf -> check [style=dashed, label="optional validation"];
}

Figure 1 Commands, ownership, and the reusable generation boundary.

motion-spec-dsl defines the .robmot language, composes its scene and FSM imports, resolves DSL semantics, owns the model classes, RDF vocabulary and manifest validation, and emits the JSON-LD RDF dataset. motion-spec consumes that boundary: its RDF parser lowers the combined dataset into compiler IR, after which code generation and runtime tooling take over. It does not define or parse the authoring language itself.

The high-level motion-spec gen and motion-spec run MODEL commands invoke the installed DSL frontend before continuing through their own stages. motion-spec build compiles one generation. motion-spec run takes either a model, which it generates and builds first, or an existing generation directory. motion-spec replay consumes the resulting run.

Generations and runs

A generation is immutable input plus reusable generated and compiled artifacts. It receives its own directory because separate generations may differ even when they came from the same model. A new generation goes to <base>/<model stem or --name>/<timestamp>/, where the base is -o or $MOTION_SPEC_GEN; with neither, gen and run stop. gen prints the generation’s path on stdout, and $MOTION_SPEC_GEN/latest points at the newest.

A generation may be executed many times. Each execution belongs under that generation’s runs/<run-id>/ directory. Runs contain runtime data only; they reference generation-owned model, controller, contract, and provenance files instead of copying them.

<base>/<model>/<timestamp>/
  generated/
    source/       authored DSL snapshot
    model/        application JSON-LD, imported graphs, scene and FSM graphs, motion IR
    controller/   generated C++ and CMake project
    contract/     schema, frame layout, and frame-log protocol
    provenance/
      generation.ld.json  one document, one named graph per tool
      derived.ld.json     what IR generation derived, and from what
  build/           reusable compiled controller
  logs/            console.log: what the DSL, stst and cmake said
  runs/<run-id>/
    logs/          frame_log.pb (zstd-packed to frame_log.pb.zst once archived),
                   console.log and health information
    files/         the environment file the run was given
    provenance/
      <run-id>.ld.json    REC record: lifecycle, host, metrics
      execution.ld.json   who ran it, the command line, the files it used and generated,
                          sampling draws
    manifest.json consumer-facing paths into the run and generation

JSON-LD artifacts use the .ld.json suffix and are loaded as RDF datasets, not treated as application JSON.

High-level commands

Table 4 Model workflow

Command

Contract

health

Report Python, executable, and target-native dependencies

gen [code] MODEL

Generate JSON-LD, IR, contracts, provenance, and C++

gen ir MODEL

Stop after JSON-LD and IR

build GENERATION

Configure and compile into GENERATION/build

run MODEL

Create a generation, build it, execute it, recover runtime RDF, and archive the run

run GENERATION

Execute an existing generation again, as a new run beside the earlier ones

rerun [GENERATION]

run for the generation latest points at (or a named one); generates and builds nothing

replay RUN

Summarize, verify, decode, or recover a recorded run

view SCENEX

Compose a scene in MuJoCo and view it, without generating or building anything

Use --prefix on build or run to add CMake package prefixes. It is repeatable for build. -o, --name, --prefix, and -j configure generation and build, so they require a .robmot model; --headless, --steps, and --run-id apply to both forms, --steps requires --headless, and both are refused for a hardware generation.

Low-level commands

The individual stages remain available for automation and debugging:

Table 5 Stage commands

Command

Input and output

check MANIFEST

Validate an application JSON-LD manifest and its imported graphs with SHACL

gen ir MODEL

Stop after lowering to IR

The high-level model commands call these capabilities through the same Python implementation; they do not maintain a second pipeline.

Validation

check resolves the application manifest’s RDF imports and SHACL shapes. Pass --meta-shacl to validate the shapes themselves against SHACL-of-SHACL. A successful generation does not replace an explicit validation step when validation is part of a release or CI contract.

Runtime recording

run creates the REC record provenance/<run-id>.ld.json before launching the executable and exports the frame-log path to it. The run is recorded there as a prov-ext:Execution with an OSLC Automation state and, once complete, a verdict; the agents that ran it, its command line and the files it used and generated go to provenance/execution.ld.json on the same run node. The log starts with a header containing the schema hash; a model that samples follows it with the seed and every draw ([sampling] seed in the robot config fixes the seed, otherwise the OS draws one). One protobuf frame per control tick follows. On completion, the CLI catalogs run-owned files, records their provenance, and verifies the archive.

The manifest is deliberately small: its files map identifies what replay and other consumers need. Lifecycle provenance lives in the REC record, runtime artifact hashes in the execution document. Everything generation recorded lives in one document, generated/provenance/generation.ld.json, with a named graph per tool that wrote into it. Every prov:atLocation is a path from the generations root ($MOTION_SPEC_GEN), such as pick_and_place/20261004T114752754313Z/generated/model/ir.json; each document states the root as its @base, so a reader resolves it to the file.

Replay

With no option, replay prints a run summary. --verify requires the run’s manifest.json: it checks that every file the manifest names exists, that the log’s schema hash matches the generation’s frame_layout.json, and that the log holds as many frames as the writer reported. A run without a manifest can still be summarized and decoded, but not verified. --jsonl streams decoded frames for external analysis.