Communicating Between Steps

Cloche offers three mechanisms for passing data between steps: environment variables injected by the daemon, a KV store accessible from both host and container steps, and a shared run folder for large files. Which one to use depends on what you’re passing and where it needs to go.

Environment Variables

The daemon injects a set of CLOCHE_* environment variables into every step. These provide context about the current run — IDs, paths, and output locations — rather than user data.

Host steps

Variable Description
CLOCHE_PROJECT_DIR Absolute path to the project directory on the host.
CLOCHE_RUN_ID Run ID for this workflow execution (e.g. a133:develop).
CLOCHE_STEP_OUTPUT Path where this step should write its output.
CLOCHE_PREV_OUTPUT Path to the output file from the immediately preceding step.
CLOCHE_TASK_ID Task ID assigned by the daemon (set for the main phase).

Container steps

Variable Description
CLOCHE_RUN_ID Run ID for this workflow execution (e.g. a133:develop).
CLOCHE_TASK_ID Task ID assigned by the daemon. Set when the run is associated with a task.
CLOCHE_ATTEMPT_ID Attempt identifier for this container run.
CLOCHE_PROJECT_DIR Working directory inside the container (/workspace).
CLOCHE_AGENT_COMMAND Overrides the default agent command inside the container.
CLOCHE_ADDR Daemon gRPC address (e.g. host.docker.internal:50051). Used by clo.
ANTHROPIC_API_KEY Passed through from the host environment if set.

KV Store

The KV store is the primary way to pass user data between steps. It is backed by the daemon’s gRPC API and is accessible from both host scripts and container steps, making it the natural choice for cross-boundary communication.

  • Host scripts use cloche get <key> and cloche set <key> <value>
  • Container steps use clo get <key> and clo set <key> <value>

Both commands require CLOCHE_TASK_ID (and CLOCHE_ATTEMPT_ID if set), which the daemon injects automatically.

# In a host script step:
cloche set pr_id 1234

# In a later container step:
pr_id=$(clo get pr_id)

Values are scoped to the run and persist for its lifetime. Any step in the run can read keys written by any earlier step.

Reading from stdin or files

cloche set and clo set accept - to read from stdin, or -f <file> to read from a file:

cloche set description -         # read from stdin (trailing newlines trimmed)
cloche set config -f config.json # read from file

Auto-seeded keys

The daemon writes several keys automatically before each step runs:

Run-level (set once at run start):

Key Value
task_id Task identifier
attempt_id Attempt identifier
workflow Current workflow name
run_id Run identifier

Step-level (updated before each step):

Key Value
prev_step Name of the step that triggered this one (empty for the entry step)
prev_step_exit Result of that step (empty for the entry step)

Step result tracking (set after each step completes):

Key Value
<workflow>:<step>:result Result of the completed step (e.g. develop:implement:result = success)

Sub-workflow results (set after a workflow_name step targeting a container workflow completes):

Key Value
child_run_id Run ID of the child container workflow; used to locate the cloche/<run-id> git branch with extracted results

All auto-seeded keys are writable — scripts can overwrite them with cloche set if needed.

Listing keys

Inside a container, clo keys lists all keys in the current attempt namespace.

Shared Run Folder

KV values are limited to 1 KB. For larger data — diffs, logs, generated files — write to the shared run folder and pass the filename through the KV store.

The run folder is at .cloche/runs/{CLOCHE_RUN_ID}/. It is a bind mount shared between the host and the run’s containers, so files written on either side are visible to the other. The daemon creates this directory at run start.

# Host script — write a large review payload
output_dir=$(cloche get temp_file_dir)
generate-feedback > "$output_dir/review-feedback.md"
cloche set review_feedback_path "$output_dir/review-feedback.md"

# Container step — read it back
feedback_path=$(clo get review_feedback_path)
cat "$feedback_path"

The built-in KV key temp_file_dir is set automatically by the daemon and points to .cloche/runs/<run-id>. Use it rather than constructing the path yourself.

Run folder cleanup

When a workflow run exits, the daemon deletes its run folder automatically. Do not store anything in .cloche/runs/ that needs to outlive the run. If a step produces artifacts that should persist (e.g. a built binary or a report), copy them to a location outside the run folder before the workflow completes.

Example: Passing Task Data Through a Pipeline

A common pattern is for a host script step to extract data from a task, write it to the shared run folder, and pass the filename via the KV store for downstream steps to consume:

# .cloche/scripts/prepare-prompt.sh (host script step)

# Read the task description from the tracker
task_id="$CLOCHE_TASK_ID"
description=$(gh issue view "$task_id" --json body -q .body)

# Write it to the shared run folder (handles files > 1 KB)
output_dir=$(cloche get temp_file_dir)
prompt_file="$output_dir/task_prompt.md"
echo "$description" > "$prompt_file"

# Store the path so downstream steps can find it
cloche set task_prompt_path "$prompt_file"

A container step later in the pipeline reads it back:

# Inside a container step (or in a prompt template via clo)
prompt_path=$(clo get task_prompt_path)
cat "$prompt_path"

This keeps large payloads out of the KV store (which is limited to 1 KB values) while still using it as the coordination mechanism.

Which mechanism to use

Need Mechanism
Run context (IDs, paths) Environment variables — already injected
Small values between steps (< 1 KB) KV store (cloche set / clo get)
Large files between steps (> 1 KB) Shared run folder + KV store for the path