Last updated: 2026-04-11
Status: v0 (implemented baseline)
Clawform runs agent work from markdown files instead of chat windows.
A program is one markdown file (*.md) representing one task.
- frontmatter is tool-owned and strict (
id,model,skills,variables) - markdown body is agent-facing and free-form
- Public command:
cf -f program.md(explicit equivalent:cf apply -f program.md, full binary form:clawform apply -f program.md) - Confirmation prompt is default in interactive shell; use
--yesto skip - One program file = one session execution
- Config path is fixed:
<cwd>/.clawform/config.json - Provider support in v0: Codex and Claude
- Live progress is on by default (
--progress offdisables,--progress plainkeeps non-interactive streaming, rich mode is used automatically in an interactive TTY) - Sandbox selector is exposed as
--sandbox auto|workspace|full-access(default:auto) plus shorthand flags--auto,--workspace, and--full-access - Session artifacts and run history are stored under
.clawform/
Path:
<cwd>/.clawform/config.json
Rules:
- when no explicit provider override is passed, exactly one provider must have
"default": true - provider
typemust be"codex"or"claude"in v0
Example:
{
"clawform": {
"providers": {
"codex": {
"type": "codex",
"default": false,
"default_model": "gpt-5-codex"
},
"claude": {
"type": "claude",
"default": true,
"default_model": "sonnet"
}
}
}
}Frontmatter (strict):
id(optional)model(optional override)skills(optional list of required provider-native skill names)variables(optional map)- key: variable name (
[A-Za-z_][A-Za-z0-9_]*) - value:
- required (no default):
NAME: {} - optional default:
NAME: { default: "value" }
- required (no default):
- key: variable name (
Program key resolution:
- use
idif present - otherwise use filename stem
Markdown body remains untyped in v0 and is interpreted by the agent.
Variable rules:
- markdown may reference variables as
${{ var.NAME }} - referenced variables must be defined in frontmatter
- apply-time
--var NAME=VALUEoverrides frontmatter default - variables without default are required at apply time
Skill rules:
skillsis a shared frontmatter list, for exampleskills: [dstack]- each listed skill name must be a single non-empty token without whitespace
- Clawform prepends explicit provider-native skill validation/invocation lines to the real session prompt before the normal apply contract
- skill command syntax is provider-specific (
$skillfor Codex,/skillfor Claude) - the injected validation line is phrased as
Fail if skill is not found. - the injected prelude also instructs the agent to write
.clawform/agent_result.jsonwithstatus: failureandreason: program_blockedbefore stopping - if the provider still surfaces a missing-skill response without writing the result artifact, Clawform uses provider-specific fallback detection and fails the apply run
- Load program + config and resolve provider + model (
-p/--provideroverrides default provider selection). - Resolve program variables (frontmatter defaults + CLI
--varoverrides). - Validate variable definitions and
${{ var.NAME }}references. - Build preview from previous run records:
- last session status/summary (if exists)
- program diff vs last session snapshot (if available)
- variable diff vs last session variable snapshot (if available)
- Ask for confirmation (interactive default; skipped by
--yes). - Clear prior run protocol files in
.clawform/and write runtime variables file (.clawform/agent_variables.json) when variables are present. - Build runtime prompt; if the program declares
skills, prepend provider-native skill invocation commands first. Inworkspaceandautomodes include explicit verdict-gate rules for sandbox-vs-program blocking. - Run provider in the current workspace (no temp workspace copy).
- Normalize provider-native events into shared progress categories, stream them to terminal, and during the run write session
commands/*andmessages/*. In debug mode, also write sessionevents.ndjson. - In
automode, allow at most one retry infull-accessmode only when current-run.clawform/agent_result.jsonreportsstatus=partial|failureandreason=sandbox_blocked(no stdout/stderr heuristic fallback). - Read agent status from
.clawform/agent_result.json(required) and validate strict status/reason schema. - Collect reported changed files from
.clawform/agent_outputs.jsonwhen that file exists and was updated in this run. - Persist run-end records (
output.md,outcome.json) and append.clawform/history/index.jsonl. - Persist program snapshot (
program.md) and variable snapshot (variables.json) on success.
- In an interactive TTY, rich progress keeps a spinner plus a live
runningorrunning: <activity>status line. - The run-start line includes the session id, execution mode, a compact
provider:modelsuffix, and when applicable a compactskills:suffix, for example🧵 <session> | workspace | codex:gpt-5-codex | skills:dstack. runningmeans the provider run is still alive. It is a liveness indicator, not the same thing as reasoning text.- The live activity suffix is derived from the highest-priority active provider item, for example
search ...,fetch ...,use ...,edit ..., orupdate plan. - Completed progress lines are normalized across Claude and Codex into shared categories such as reasoning (
💭), assistant text (💬), search (🔎), fetch (🌐), command (❱), file change (✏️), plan update (update plan | ...), generic tool (🔧), and unknown provider event (📦). - In
plainmode, Clawform prints stable progress lines without the interactive spinner/status renderer. - Unknown tool-like and non-tool provider items are rendered through generic fallbacks instead of being silently dropped.
- Interrupting with
Ctrl+Cshould surface cancellation rather than a raw provider stdout/stderr dump.
Clawform keeps local data for three explicit behaviors:
Path aliases used in this section:
<protocol_root> = <cwd>/.clawform<history_index> = <protocol_root>/history/index.jsonl<session_root> = <protocol_root>/programs/<program_id>/sessions/<session_id><last_session_root> = <protocol_root>/programs/<program_id>/sessions/<last_session_id><last_session_id>=session_idfrom the newest<history_index>record for the sameprogram_id
- Before provider execution, write
<protocol_root>/agent_variables.json(when variables exist) so the agent can read resolved${{ var.NAME }}values for this run. - After provider execution, read agent protocol files from
<protocol_root>/:- required:
agent_result.json - expected for file reporting:
agent_outputs.json - optional summary:
agent_output.md
- required:
- After apply completes, persist session/history files (
<session_root>/program.md,<session_root>/variables.json,<session_root>/output.md,<session_root>/outcome.json,<history_index>) so the next run can compute diffs and include previous-run status/summary.
During the current run:
- Write
<protocol_root>/agent_variables.json(when variables exist); the agent reads this file for resolved${{ var.NAME }}values. - Write
<session_root>/commands/*and<session_root>/messages/*as per-session execution artifacts. - In debug mode, also write
<session_root>/events.ndjsonas a per-session event trace artifact. - Read
<protocol_root>/agent_result.json,<protocol_root>/agent_outputs.json, and optional<protocol_root>/agent_output.mdat run end to determine status, changed files, and summary.
On the next run of the same program:
- Read
<history_index>, select the newest record for the sameprogram_id, and use itssession_idas<last_session_id>. - Read
<last_session_root>/program.mdand compare it to the current program file to compute program-text diff. - Read
<last_session_root>/variables.jsonand compare it to current resolved variables to compute variable diff. - Read
<last_session_root>/output.mdas the prior run summary shown in plan preview and included in the runtime prompt.
For audit/debug visibility:
- Write
<session_root>/outcome.jsonas the final machine-readable run outcome. It is for inspection and is not currently used as a control input for future apply decisions.
| Data path | Scope | Why we store it | When it is used |
|---|---|---|---|
<protocol_root>/agent_variables.json |
Workspace-global scratch file for the currently running apply (overwritten on each apply) | Provide resolved runtime variables to the agent | Read by the agent during that same apply run |
<protocol_root>/agent_result.json |
Workspace-global scratch file for the currently running apply (overwritten on each apply) | Receive final structured run verdict (status, optional reason, message) where reason is strict enum (sandbox_blocked or program_blocked) |
Read by Clawform at run end; in sandbox auto mode also used as the only retry signal source, only if file mtime is from this run |
<protocol_root>/agent_outputs.json |
Workspace-global scratch file for the currently running apply (overwritten on each apply) | Receive changed-file list from the agent | Read by Clawform at run end for file summary/history, only if file mtime is from this run |
<protocol_root>/agent_output.md |
Workspace-global scratch file for the currently running apply (optional; overwritten on each apply) | Receive agent-written summary text | Read by Clawform at run end; then copied into session output.md |
<session_root>/events.ndjson |
Per-session (<program_id>/<session_id>), debug mode only |
Preserve normalized provider events plus raw source lines for this session | Used for audit/debug flows and event-based validation |
<session_root>/commands/*.txt |
Per-session (<program_id>/<session_id>) |
Preserve command output artifacts for this session | Used for progress drilldown and debugging |
<session_root>/messages/*.md |
Per-session (<program_id>/<session_id>) |
Preserve assistant/message artifacts for this session | Used for progress drilldown and fallback summary source |
<session_root>/output.md |
Per-session (<program_id>/<session_id>) |
Store stable summary artifact for this session | Used on next run of same program_id for preview/prompt reference |
<session_root>/program.md |
Per-session (<program_id>/<session_id>) |
Snapshot program text that produced this session | Used on next run of same program_id to compute program diff |
<session_root>/variables.json |
Per-session (<program_id>/<session_id>) |
Snapshot resolved variables for this session | Used on next run of same program_id to compute variables diff |
<session_root>/outcome.json |
Per-session (<program_id>/<session_id>) |
Store machine-readable session outcome with status/error/token/file counters | Inspection/audit; not currently a control input |
<history_index> |
Workspace-global append-only index | Store per-run summary metadata (status, summary_short, file/token stats) | Loaded at next run start for previous-run status/summary/stats in preview and prompt |
Compatibility behavior:
- No read fallback is used for
agent_summary.md. - Current apply reads only the current protocol files documented in this section.
- Sandbox auto-retry does not parse provider stdout/stderr for sandbox heuristics; it only trusts current-run
agent_result.json.
Current limitation:
.clawform/agent_*.json|mdprotocol files are workspace-global and not namespaced byprogram_id/session_id.- This can conflict if two applies run concurrently in the same workspace.
- TODO: move current-run protocol files to per-session paths (for example:
<session_root>/protocol/agent_result.json,agent_outputs.json,agent_output.md,agent_variables.json).
Current apply does not persist:
prompt.mdplan.json- provider stdout/stderr artifact logs
events.ndjsonon non-debug runs
Protocol file: <protocol_root>/agent_result.json
Expected shape:
{
"status": "success|partial|failure",
"reason": "sandbox_blocked|program_blocked",
"message": "short human-readable summary"
}Rules:
statusis required and strict enum:success | partial | failure.reasonis strict enum:sandbox_blocked | program_blocked.reasonis required forpartialandfailure; omitted forsuccess.- Unknown
reasonvalues are rejected when Clawform parsesagent_result.json. - In
workspaceandautomodes, runtime prompt enforces verdict gate semantics:- first restriction symptom triggers block-cause classification
- any sandbox evidence (including non-fatal permission/network warnings), mixed evidence, or uncertainty =>
reason: sandbox_blocked reason: program_blockedonly when zero restriction symptoms appeared and one read-only check confirms an independent non-sandbox cause- no workaround/fallback commands before writing the verdict
Applies only when sandbox mode is auto:
- First pass runs in
workspacemode. - One retry in
full-accessmode is allowed only when current-runagent_result.jsonreports:statusinpartial|failurereason: sandbox_blocked
- No retry is triggered from command-output text heuristics.
- When retry is triggered, Clawform emits a retry-decision progress line and then launches one
full-accessattempt.
Steps to reproduce:
- Start
cf -f <program-a.md>in one terminal (same workspace). - While it is still running, start
cf -f <program-b.md>in another terminal (same workspace). - Both runs write/read
.clawform/agent_result.json/.clawform/agent_outputs.json/.clawform/agent_output.md.
Expected:
- Each run uses isolated protocol files for its own session.
Actual:
- Protocol files are shared workspace-global scratch paths and can overwrite each other.
These items are intentionally deferred. Each item describes desired product capability, not implementation.
- Memory support
Goal: support durable context across sessions with predictable usage rules. - Plan support
Goal: support planning as a first-class workflow, separate from execution. - Persisted provider trace artifacts
Goal: optionally persist canonical normalized provider events for audit/debug flows without requiring raw provider stdout parsing. - Changes/diff reliability and consistency
Goal: for the same session, preview, apply output, debug output, and history should report the same changed-file set and line counts, with generated/noise files handled consistently. - Agent-reported changes as single source of truth
Goal: remove local diff-based change reporting and use agent-reported change data consistently across apply, debug, and history. - MCP and broader tool integration model
Goal: support richer external tool and integration patterns. - Multi-agent orchestration model
Goal: support coordinated workflows that involve more than one agent. - Additional providers beyond Codex and Claude
Goal: support multiple model providers in a consistent user experience. - Improved session storage and retrieval performance
Goal: keep history/state operations fast and scalable as usage grows. - Session-scoped protocol files for concurrent apply safety
Goal: move.clawform/agent_*.json|mdto per-session protocol paths. - Optional Claude
--bareprovider mode Goal: add a more deterministic no-ambient-context Claude launch mode as an explicit provider option without making it required for v1.