fabro/docs-internal/run-directory-keys.md
Bryan Helmkamp e79f337077
Make workflow state fully derivable from events
Complete the remaining event coverage from the events-as-source-of-truth plan.
Add response and failure-signature snapshots to stage.completed,
enrich retro.started and retro.completed with prompt/response data,
and remove the stale script field from stage.started.

Also update the internal event and run-directory docs so they match
current event payloads and derivation rules.
2026-04-02 08:33:42 -07:00

298 lines
14 KiB
Markdown

# Run Directory Keys
All keys that may be written to the run store during a workflow execution, with event source mappings.
## 1. `_init.json`
Store initialization metadata. Written when the store is created.
No event source — written directly at store creation time.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `run_id` | ULID string | — |
| `created_at` | RFC 3339 timestamp | — |
| `db_prefix` | SlateDB key prefix | — |
| `run_dir` | path to run directory (optional) | — |
## 2. `run.json`
Run configuration snapshot. Written at run creation.
No single event carries this data. The `run.started` event has a subset (`name`, `run_id`, `base_branch`, `base_sha`, `run_branch`, `goal`) but not `settings`, `graph`, or `labels`.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `run_id` | ULID string | `run.started` → `envelope.run_id` |
| `created_at` | RFC 3339 timestamp | — |
| `settings` | full Settings object | — |
| `graph` | parsed workflow graph | — |
| `workflow_slug` | workflow slug (optional) | — |
| `working_directory` | path string | — |
| `host_repo_path` | original host repo path (optional) | — |
| `base_branch` | base git branch (optional) | `run.started` → `properties.base_branch` |
| `labels` | string key-value map (optional) | — |
## 3. `start.json`
Start timestamp and git context. Written when execution begins.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `run_id` | ULID string | `run.started` → `envelope.run_id` |
| `start_time` | RFC 3339 timestamp | `run.started` → `envelope.ts` |
| `run_branch` | git branch for the run (optional) | `run.started` → `properties.run_branch` |
| `base_sha` | base commit SHA (optional) | `run.started` → `properties.base_sha` |
## 4. `checkpoint.json`
Latest execution state. Updated after each node completes.
Checkpoint is an accumulated snapshot built from multiple events over time. Individual fields map to specific events, but the full object is never in a single event.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `timestamp` | RFC 3339 timestamp | — (written at checkpoint time) |
| `current_node` | node being executed | `stage.started` → `envelope.node_id` |
| `completed_nodes` | list of completed node ids | accumulated from `stage.completed` → `envelope.node_id` |
| `node_retries` | map of node id → retry count | accumulated from `stage.retrying` → `envelope.node_id` + `properties.attempt` |
| `context_values` | map of context key → JSON value | — (internal engine state) |
| `node_outcomes` | map of node id → outcome | see below |
| `next_node_id` | pre-selected next node (optional) | `edge.selected` → `properties.to_node` |
| `git_commit_sha` | current HEAD SHA (optional) | `checkpoint.completed` → `properties.git_commit_sha` |
| `loop_failure_signatures` | failure signature → count (optional) | — (internal engine state) |
| `restart_failure_signatures` | failure signature → count (optional) | — (internal engine state) |
| `node_visits` | node id → visit count (optional) | — (internal engine state) |
**`node_outcomes[node_id]`** — each outcome maps to `stage.completed`:
| Field | Description | Event Source |
|-------|-------------|--------------|
| `status` | `"success"` / `"fail"` / `"skipped"` / `"partial_success"` / `"retry"` | `stage.completed` → `properties.status` |
| `preferred_label` | edge label hint (optional) | `stage.completed` → `properties.preferred_label` |
| `suggested_next_ids` | successor node ids (optional) | `stage.completed` → `properties.suggested_next_ids` |
| `context_updates` | context key → JSON value (optional) | — (not in event) |
| `jump_to_node` | non-edge jump target (optional) | — (not in event) |
| `notes` | free-text notes (optional) | `stage.completed` → `properties.notes` |
| `failure.message` | error description | `stage.completed` → `properties.error` (flattened) |
| `failure.failure_class` | failure category | `stage.completed` → `properties.failure_class` (flattened) |
| `failure.failure_signature` | dedup key (optional) | `stage.completed` → `properties.failure_signature` (flattened) |
| `usage.model` | model identifier | `stage.completed` → `properties.usage.model` |
| `usage.input_tokens` | input token count | `stage.completed` → `properties.usage.input_tokens` |
| `usage.output_tokens` | output token count | `stage.completed` → `properties.usage.output_tokens` |
| `usage.cache_read_tokens` | cache read tokens (optional) | `stage.completed` → `properties.usage.cache_read_tokens` |
| `usage.cache_write_tokens` | cache write tokens (optional) | `stage.completed` → `properties.usage.cache_write_tokens` |
| `usage.reasoning_tokens` | reasoning tokens (optional) | `stage.completed` → `properties.usage.reasoning_tokens` |
| `usage.speed` | speed tier (optional) | `stage.completed` → `properties.usage.speed` |
| `usage.cost` | estimated cost in USD (optional) | `stage.completed` → `properties.usage.cost` |
| `files_touched` | file paths modified (optional) | `stage.completed` → `properties.files_touched` |
| `duration_ms` | stage duration (optional) | `stage.completed` → `properties.duration_ms` |
## 5. `conclusion.json`
Final run summary. Written when the run finishes.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `timestamp` | RFC 3339 timestamp | — (written at conclusion time) |
| `status` | final status | `run.completed` → `properties.status` |
| `duration_ms` | total run duration | `run.completed` → `properties.duration_ms` |
| `failure_reason` | error message (optional) | `run.failed` → `properties.error` |
| `final_git_commit_sha` | final HEAD SHA (optional) | `run.completed` → `properties.final_git_commit_sha` |
| `stages` | list of stage summaries (optional) | — (aggregated, not in events) |
| `stages[].stage_id` | node id | `stage.completed` → `envelope.node_id` |
| `stages[].stage_label` | display label | `stage.completed` → `envelope.node_label` |
| `stages[].duration_ms` | stage duration | `stage.completed` → `properties.duration_ms` |
| `stages[].cost` | cost in USD (optional) | `stage.completed` → `properties.usage.cost` |
| `stages[].retries` | retry count | accumulated from `stage.retrying` events |
| `total_cost` | aggregate cost (optional) | `run.completed` → `properties.total_cost` |
| `total_retries` | aggregate retries | — (aggregated from stage events) |
| `total_input_tokens` | aggregate input tokens | `run.completed` → `properties.usage.input_tokens` |
| `total_output_tokens` | aggregate output tokens | `run.completed` → `properties.usage.output_tokens` |
| `total_cache_read_tokens` | aggregate cache read tokens | `run.completed` → `properties.usage.cache_read_tokens` |
| `total_cache_write_tokens` | aggregate cache write tokens | `run.completed` → `properties.usage.cache_write_tokens` |
| `total_reasoning_tokens` | aggregate reasoning tokens | `run.completed` → `properties.usage.reasoning_tokens` |
| `has_pricing` | whether cost data is available | — (derived from `total_cost`) |
## 6. `retro.json`
Retrospective analysis. Written after the retro agent completes.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `run_id` | ULID string | `retro.completed` → `envelope.run_id` |
| `workflow_name` | workflow name | `run.started` → `properties.name` |
| `goal` | workflow goal text | `run.started` → `properties.goal` |
| `timestamp` | RFC 3339 timestamp | `retro.completed` → `envelope.ts` |
| `smoothness` | rating (optional) | `retro.completed` → `properties.retro.smoothness` |
| `stages` | list of stage retro objects | `retro.completed` → `properties.retro.stages` |
| `stats` | aggregate stats object | `retro.completed` → `properties.retro.stats` |
| `intent` | what the run intended to do (optional) | `retro.completed` → `properties.retro.intent` |
| `outcome` | what actually happened (optional) | `retro.completed` → `properties.retro.outcome` |
| `learnings` | list of learnings (optional) | `retro.completed` → `properties.retro.learnings` |
| `friction_points` | list of friction points (optional) | `retro.completed` → `properties.retro.friction_points` |
| `open_items` | list of open items (optional) | `retro.completed` → `properties.retro.open_items` |
## 7. `sandbox.json`
Sandbox environment details. Written when the sandbox is ready.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `provider` | provider name | `sandbox.initialized` → `properties.provider` |
| `working_directory` | working directory in sandbox | `sandbox.initialized` → `properties.working_directory` |
| `identifier` | instance identifier (optional) | `sandbox.initialized` → `properties.identifier` |
| `host_working_directory` | host-side path (optional) | `sandbox.initialized` → `properties.host_working_directory` |
| `container_mount_point` | container mount point (optional) | `sandbox.initialized` → `properties.container_mount_point` |
## 8. `workflow.fabro`
Raw Graphviz dot source for the workflow graph. Plain text, not JSON.
Event source: `run.created` → `properties.workflow_source`
## 9. `workflow.toml`
Workflow configuration in TOML format. Same schema as `settings` in `run.json`.
Event source: `run.created` → `properties.workflow_config`
## 10. `checkpoints/{seq:04}-{epoch_ms}.json`
Checkpoint history snapshots. Same schema as `checkpoint.json` (#4).
Each snapshot is written on `checkpoint.completed` events.
## 11. `nodes/{node_id}/prompt.md`
Prompt sent to the LLM for agent or prompt nodes. Plain text/markdown, not JSON.
Partial event source: `stage.prompt` → `properties.text` carries the rendered prompt text.
## 12. `nodes/{node_id}/response.md`
Response received from the LLM. Plain text/markdown, not JSON.
Reconstructable from `agent.message` → `properties.text` events (one per LLM turn), but the file contains only the final response.
## 13. `nodes/{node_id}/status.json`
Node execution status. Written when a node completes.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `status` | stage status | `stage.completed` → `properties.status` |
| `notes` | free-text notes (optional) | `stage.completed` → `properties.notes` |
| `failure_reason` | error message (optional) | `stage.completed` → `properties.error` (flattened from failure) |
| `timestamp` | RFC 3339 timestamp | `stage.completed` → `envelope.ts` |
## 14. `nodes/{node_id}/stdout.log`
Standard output from command nodes. Plain text, not JSON.
No event source — captured from sandbox exec, not emitted as events.
## 15. `nodes/{node_id}/stderr.log`
Standard error from command nodes. Plain text, not JSON.
No event source — captured from sandbox exec, not emitted as events.
## 16. `nodes/{node_id}/cli_stdout.log`
Standard output from CLI-backend LLM invocations. Plain text, not JSON.
No event source — captured from CLI subprocess, not emitted as events.
## 17. `nodes/{node_id}/cli_stderr.log`
Standard error from CLI-backend LLM invocations. Plain text, not JSON.
No event source — captured from CLI subprocess, not emitted as events.
## 18. `nodes/{node_id}/diff.patch`
Git diff of sandbox changes made by the node. Plain text unified diff, not JSON.
No event source — generated from git at checkpoint time.
## 19. `nodes/{node_id}/provider_used.json`
LLM provider metadata. Written for agent, prompt, and CLI-backend nodes.
No direct event. Closest: `agent.failover` carries `from_provider`/`to_provider`/`from_model`/`to_model`, but only on failover. The initial provider choice is not emitted as an event.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `mode` | `"agent"` / `"prompt"` / `"cli"` | — |
| `provider` | provider name | — |
| `model` | model identifier | `stage.completed` → `properties.usage.model` (indirect) |
| `command` | CLI command (only when mode=cli) | — |
## 20. `nodes/{node_id}/script_invocation.json`
Command node invocation details. Written before the command runs.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `command` | shell command or script body | `stage.started` → `properties.script` (when handler_type is command) |
| `language` | `"shell"` / `"python"` | — |
| `timeout_ms` | timeout in milliseconds (null if none) | — |
## 21. `nodes/{node_id}/script_timing.json`
Command node execution timing. Written after the command completes.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `duration_ms` | execution duration | `stage.completed` → `properties.duration_ms` |
| `exit_code` | process exit code (null if timed out) | — |
| `timed_out` | whether command was killed by timeout | — |
## 22. `nodes/{node_id}/parallel_results.json`
Results from parallel branch execution. Written by the parallel handler.
Array of objects:
| Field | Description | Event Source |
|-------|-------------|--------------|
| `id` | branch node id | `parallel.branch.completed` → `envelope.node_id` |
| `status` | status string | `parallel.branch.completed` → `properties.status` |
| `head_sha` | git HEAD SHA (optional) | `parallel.branch.completed` → `properties.head_sha` |
## 23. `retro/prompt.md`
Prompt sent to the retro agent. Plain text/markdown, not JSON.
Event source: `retro.started` → `properties.prompt`
## 24. `retro/response.md`
Response received from the retro agent. Plain text/markdown, not JSON.
Event source: `retro.completed` → `properties.response`
## 25. `retro/status.json`
Retro agent execution status.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `outcome` | `"success"` / `"failure"` | `retro.completed` or `retro.failed` (inferred from which event fires) |
| `failure_reason` | error message (null on success) | `retro.failed` → `properties.error` |
| `timestamp` | RFC 3339 timestamp | `retro.completed` → `envelope.ts` or `retro.failed` → `envelope.ts` |
## 26. `retro/provider_used.json`
Retro agent LLM provider metadata.
| Field | Description | Event Source |
|-------|-------------|--------------|
| `mode` | execution mode | constant `"agent"` plus `retro.started` context |
| `provider` | LLM provider | `retro.started` → `properties.provider` |
| `model` | model identifier | `retro.started` → `properties.model` |
---
**Node visit directories:** The first visit writes to `nodes/{node_id}/`. Subsequent visits write to `nodes/{node_id}-visit_{N}/` where N is the visit number.