Runtime artifacts

Long-running orchestration tools usually split telemetry you watch while something runs from evidence you keep after it stops. The first answers “what is happening now?”; the second answers “what happened, in enough detail to debug or audit later?” Jaiph does the same.

For Jaiph, live observation is the __JAIPH_EVENT__ JSON line protocol on the workflow runner’s stderr (what the interactive CLI and Hooks consume). Durable observation is a directory tree on disk: step captures, an append-only summary timeline, optional inbox copies, and a writable artifacts/ folder for anything workflows publish explicitly.

When you run a workflow, or jaiph test executes workflows inside test blocks, NodeWorkflowRuntime materializes that durable tree. jaiph run defaults to <workspace>/.jaiph/runs/; override with run.logs_dir or JAIPH_RUNS_DIR (see Configuration — Run keys). The test runner uses its own ephemeral runs root under JAIPH_RUNS_DIR so normal workspace runs are not overwritten — see Configuration — Testing with jaiph test. The layout below matches what the runtime creates in the constructor (see Architecture — Durable artifact layout). In Docker mode, paths inside recorded events may use container prefixes (/jaiph/run/…); the CLI maps them to host paths when reporting failures — see Sandboxing — Path remapping.

Run directory layout

The runtime uses a UTC-dated hierarchy. Each run gets its own folder: UTC date, then UTC time plus a basename used only for naming (not a path): JAIPH_SOURCE_FILE when set in the environment (the CLI and node-workflow-runner set this to the entry file basename), otherwise basename(graph.entryFile) from the parsed graph.

.jaiph/runs/
  <YYYY-MM-DD>/                       # UTC date (see NodeWorkflowRuntime)
    <HH-MM-SS>-<source-basename>/       # UTC time + basename (see above)
      000001-module__step.out          # stdout capture per step (6-digit seq prefix)
      000001-module__step.err          # stderr capture (may be empty)
      artifacts/                       # user-published files (`jaiphlang/artifacts`); `JAIPH_ARTIFACTS_DIR`
      inbox/                           # audit copies of routed channel payloads (optional)
      heartbeat                        # liveness: epoch ms, refreshed about every 10s
      return_value.txt                 # `runDefault` only: status 0 and `returnValue` defined (may be "")
      run_summary.jsonl                # durable event timeline (JSON Lines)

Sequence numbers in those filenames are monotonic and unique per run. RuntimeEventEmitter owns a single in-memory counter (allocStepSeq) that advances whenever a step allocates paired capture files: executeManagedStep (nested workflow / rule, script references, inline scripts, and shell lines run via sh -c) plus prompt steps (which call allocStepSeq inside emitPromptStepStart). Ordinary log, logerr, fail, send, and most const bindings do not open new numbered .out/.err pairs — they still emit LOG/LOGERR or INBOX_ENQUEUE records (and related lines) into run_summary.jsonl where applicable. There is no .seq file in the run directory. For the live vs durable split, see Architecture — Contracts: __JAIPH_EVENT__ on stderr is the streaming path; run_summary.jsonl is the durable timeline.

What each artifact is for

Keeping runs out of git

Run jaiph init to add .jaiph/.gitignore entries for runs and tmp under .jaiph/. You can mirror those paths in a root .gitignore if you prefer.