From 157ec03a18a782fee56ad7b782d93e78442ae420 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Thu, 30 Apr 2026 08:35:53 -0400 Subject: [PATCH] docs: sync public docs to recent runtime changes Reflect Docker as the default sandbox provider, add `skip_clone` for clone-based providers, document the `[run.sandbox.docker]` config table, and update tutorial command lines from `files-internal/...` to `docs/internal/...`. Bump the docs skill watermark to the latest synced commit. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/docs/watermark | 2 +- docs/public/administration/sandboxing.mdx | 2 +- docs/public/core-concepts/how-fabro-works.mdx | 6 +-- docs/public/core-concepts/models.mdx | 4 +- docs/public/examples/solitaire.mdx | 28 ++++++------ docs/public/execution/checkpoints.mdx | 4 +- docs/public/execution/devcontainers.mdx | 6 +-- docs/public/execution/environments.mdx | 44 +++++++++++++------ docs/public/execution/run-configuration.mdx | 28 +++++++++++- docs/public/integrations/daytona.mdx | 17 +++++-- docs/public/reference/dot-language.mdx | 2 +- docs/public/tutorials/branch-loop.mdx | 2 +- docs/public/tutorials/ensemble.mdx | 2 +- docs/public/tutorials/hello-world.mdx | 6 +-- docs/public/tutorials/multi-model.mdx | 2 +- docs/public/tutorials/parallel-review.mdx | 2 +- docs/public/tutorials/plan-implement.mdx | 8 ++-- docs/public/tutorials/sub-workflow.mdx | 2 +- 18 files changed, 111 insertions(+), 56 deletions(-) diff --git a/.claude/skills/docs/watermark b/.claude/skills/docs/watermark index 5d44dfced..24ba74587 100644 --- a/.claude/skills/docs/watermark +++ b/.claude/skills/docs/watermark @@ -1 +1 @@ -d2cc37c615894d56ef672d00004ce13ce3b328c2 +c8185eaee6797e5f48963fcfff688838c5ff497d diff --git a/docs/public/administration/sandboxing.mdx b/docs/public/administration/sandboxing.mdx index 377c2f4ce..d518c558f 100644 --- a/docs/public/administration/sandboxing.mdx +++ b/docs/public/administration/sandboxing.mdx @@ -9,7 +9,7 @@ Fabro supports three sandbox providers: `local` (no isolation), `docker` (contai ## Network access control -For cloud sandboxes (Daytona), you can control outbound network access with the `network` field in `[sandbox.daytona]`. Three modes are available: `"allow_all"` (default), `"block"`, and `{ allow_list = ["..."] }` for CIDR-based egress filtering. +For cloud sandboxes (Daytona), you can control outbound network access with the `network` field in `[run.sandbox.daytona]`. Three modes are available: `"allow_all"` (default), `"block"`, and `{ allow_list = ["..."] }` for CIDR-based egress filtering. Server defaults in `settings.toml` apply when a run config doesn't specify `network`. Individual run configs can override the server default. diff --git a/docs/public/core-concepts/how-fabro-works.mdx b/docs/public/core-concepts/how-fabro-works.mdx index 288e7796a..6b724d182 100644 --- a/docs/public/core-concepts/how-fabro-works.mdx +++ b/docs/public/core-concepts/how-fabro-works.mdx @@ -67,12 +67,12 @@ When the engine hits a [parallel fan-out node](/workflows/stages-and-nodes#paral ## Sandboxes -Node handlers execute tools (bash commands, file edits) inside a **sandbox**. Fabro supports six sandbox providers: +Node handlers execute tools (bash commands, file edits) inside a **sandbox**. Fabro supports three sandbox providers: | Sandbox | Description | |---|---| -| `local` | Tools run directly on the host machine (default) | -| `docker` | Tools run inside a Docker container | +| `docker` | Tools run inside a Docker container (default) | +| `local` | Tools run directly on the host machine | | `daytona` | Tools run in a cloud VM with SSH access | The sandbox is configured per-run via CLI flags (`--sandbox docker`) or the run config TOML. See [Environments](/execution/environments) for details on each provider. diff --git a/docs/public/core-concepts/models.mdx b/docs/public/core-concepts/models.mdx index 7ea0f25f5..5db0d6612 100644 --- a/docs/public/core-concepts/models.mdx +++ b/docs/public/core-concepts/models.mdx @@ -81,8 +81,8 @@ Model stylesheets set per-node models inside the workflow graph, but you can als Pass `--model` and optionally `--provider` to `fabro run`: ```bash -fabro run files-internal/demo/01-hello.fabro --model claude-opus-4-6 -fabro run files-internal/demo/04-pipeline.fabro --model gemini-3.1-pro-preview +fabro run docs/internal/demo/01-hello.fabro --model claude-opus-4-6 +fabro run docs/internal/demo/04-pipeline.fabro --model gemini-3.1-pro-preview ``` These flags set the default model for all nodes that don't have an explicit model assigned via a stylesheet. The provider is automatically inferred from the model catalog — you only need `--provider` for models not in the catalog or to force a specific provider. diff --git a/docs/public/examples/solitaire.mdx b/docs/public/examples/solitaire.mdx index de28f0feb..4a254837a 100644 --- a/docs/public/examples/solitaire.mdx +++ b/docs/public/examples/solitaire.mdx @@ -255,28 +255,30 @@ This is more reliable than a single "implement everything" node because: Pair the workflow with a run config for repeatable execution: ```toml title="run.toml" -version = 1 -goal = "Build a terminal-based solitaire (Klondike) game in Python" +_version = 1 + +[workflow] graph = "build-solitaire.fabro" -[llm] -model = "claude-sonnet-4-5" +[run] +goal = "Build a terminal-based solitaire (Klondike) game in Python" + +[run.model] +name = "claude-sonnet-4-5" provider = "anthropic" +fallbacks = ["openai", "gemini"] -[llm.fallbacks] -anthropic = ["openai", "gemini"] +[[run.prepare.steps]] +script = "python3 -m venv .venv && . .venv/bin/activate && pip install pytest curses" -[setup] -commands = ["python3 -m venv .venv && . .venv/bin/activate && pip install pytest curses"] - -[sandbox] +[run.sandbox] provider = "daytona" -[sandbox.daytona.snapshot] +[run.sandbox.daytona.snapshot] name = "python-dev" cpu = 4 -memory = 8 -disk = 20 +memory = "8GB" +disk = "20GB" dockerfile = "FROM python:3.12-slim\nRUN apt-get update && apt-get install -y git libncurses-dev" ``` diff --git a/docs/public/execution/checkpoints.mdx b/docs/public/execution/checkpoints.mdx index e64346690..fb75f6620 100644 --- a/docs/public/execution/checkpoints.mdx +++ b/docs/public/execution/checkpoints.mdx @@ -18,10 +18,10 @@ The run branch is a regular Git branch that grows one commit per completed node. ### Run branch commits -After each node finishes, Fabro stages file changes and creates a commit on the run branch. Files matching `[checkpoint] exclude_globs` patterns (configured in [run.toml](/execution/run-configuration#checkpoint) or [settings.toml](/administration/server-configuration#checkpoint-section)) are excluded from staging: +After each node finishes, Fabro stages file changes and creates a commit on the run branch. Files matching `[run.checkpoint] exclude_globs` patterns (configured in [run.toml](/execution/run-configuration#runcheckpoint) or [settings.toml](/administration/server-configuration#runcheckpoint-section)) are excluded from staging: ``` -fabro(01JKXYZ...): plan (success) +fabro(01JKXYZ...): plan (succeeded) Fabro-Run: 01JKXYZ... Fabro-Completed: 2 diff --git a/docs/public/execution/devcontainers.mdx b/docs/public/execution/devcontainers.mdx index c98e8bbf3..72916a59f 100644 --- a/docs/public/execution/devcontainers.mdx +++ b/docs/public/execution/devcontainers.mdx @@ -30,7 +30,7 @@ Fabro looks for `.devcontainer/devcontainer.json` in the repository root. If fou | `onCreateCommand` | Runs inside the sandbox after it's created | | `postCreateCommand` | Runs after `onCreateCommand` completes | | `postStartCommand` | Runs after the sandbox starts | -| `containerEnv` | Merged into sandbox environment variables (TOML `[sandbox.env]` values take precedence on key collisions) | +| `containerEnv` | Merged into sandbox environment variables (TOML `[run.sandbox.env]` values take precedence on key collisions) | Lifecycle commands (`onCreateCommand`, `postCreateCommand`, `postStartCommand`) execute sequentially inside the sandbox. If any command fails, the run aborts before the workflow starts. @@ -40,6 +40,6 @@ Fabro detects and reports unsupported `COPY` and `ADD` instructions in devcontai ## Interaction with other sandbox settings -When `devcontainer = true`, the devcontainer Dockerfile overrides any `snapshot.dockerfile` setting in `[sandbox.daytona]`. Other Daytona settings (`cpu`, `memory`, `disk`, `auto_stop_interval`, `labels`) still apply. +When `devcontainer = true`, the devcontainer Dockerfile overrides any `snapshot.dockerfile` setting in `[run.sandbox.daytona]`. Other Daytona settings (`cpu`, `memory`, `disk`, `auto_stop_interval`, `labels`) still apply. -Environment variables from `containerEnv` in the devcontainer config are merged with `[sandbox.env]` from the TOML config. On key collisions, the TOML config wins. +Environment variables from `containerEnv` in the devcontainer config are merged with `[run.sandbox.env]` from the TOML config. On key collisions, the TOML config wins. diff --git a/docs/public/execution/environments.mdx b/docs/public/execution/environments.mdx index 2cb9cabb0..abffb5df5 100644 --- a/docs/public/execution/environments.mdx +++ b/docs/public/execution/environments.mdx @@ -30,11 +30,11 @@ fabro run workflow.fabro --sandbox daytona provider = "daytona" ``` -The precedence order is: CLI flag > run config TOML > server defaults > built-in default (`local`). +The precedence order is: CLI flag > run config TOML > server defaults > built-in default (`docker`). ## Local -The local sandbox runs all tool operations directly on the host machine. It's the default and the simplest option — no setup required beyond the Fabro binary itself. +The local sandbox runs all tool operations directly on the host machine. Use it for trusted workflows, local development, or runs that must operate directly on the current working tree. ### How it works @@ -53,33 +53,48 @@ The local sandbox offers no isolation. Agents can read and modify any file on th ## Docker -The Docker sandbox runs all tool operations inside a Docker container. Docker runs use a provider-owned workspace in the container; when the run has a GitHub origin, Fabro clones that repository into the workspace. +The Docker sandbox runs all tool operations inside a Docker container. Docker is the built-in default runtime provider. Docker runs use a provider-owned workspace in the container; when the run has a GitHub origin, Fabro clones that repository into the workspace. ### Prerequisites - Docker Engine running on the host -- The configured image available locally (or `auto_pull` enabled) +- The configured image available locally, or a Docker client that can pull it +- GitHub credentials configured when cloning private repositories ### How it works - **Container lifecycle** — On `initialize()`, Fabro pulls the image (if needed), creates a container with `sleep infinity`, and starts it. On `cleanup()`, Fabro stops and removes the container. - **Working directory** — Docker runs use a provider-owned workspace inside the container. When a run has a GitHub origin, Fabro clones that repository into the workspace instead of bind-mounting the host source tree. +- **Git clone** — Docker and Daytona accept GitHub origins for automatic cloning. If the run has a present non-GitHub origin, set `skip_clone = true` or switch to `local`. - **Commands** — Executed via `docker exec` with `/bin/bash -c` inside the container. Timeout and cancellation are supported. - **File writes** — Use the Docker API's tar upload to avoid shell escaping issues with special characters. - **Platform detection** — The container's `uname -r` is cached at startup. ### Configuration -The Docker sandbox is configured through the `DockerSandboxConfig`: +Configure Docker through `[run.sandbox.docker]`: + +```toml title="run.toml" +[run.sandbox] +provider = "docker" + +[run.sandbox.docker] +image = "buildpack-deps:noble" +network_mode = "bridge" +memory_limit = "4GB" +cpu_quota = 200000 +skip_clone = false +``` | Setting | Default | Description | |---|---|---| -| `image` | `fabro-agent:latest` | Docker image to use | +| `image` | `buildpack-deps:noble` | Docker image to use | | `network_mode` | `bridge` | Docker network mode | -| `memory_limit` | unlimited | Memory limit in bytes | -| `cpu_quota` | unlimited | CPU quota (microseconds per 100ms period) | -| `auto_pull` | `true` | Pull the image if not found locally | -| `env_vars` | `[]` | Additional `KEY=VALUE` environment variables | +| `memory_limit` | `4GB` | Memory limit | +| `cpu_quota` | `200000` | CPU quota (microseconds per 100ms period) | +| `skip_clone` | `false` | Create an empty workspace instead of cloning the run's GitHub origin | + +When `skip_clone = true`, the sandbox starts with an empty provider workspace. Use [prepare steps](/execution/run-configuration#runprepare) to clone or create any files the workflow needs. ### Preserving the container @@ -106,13 +121,13 @@ The Daytona sandbox runs all tool operations inside a cloud-hosted VM managed by ### Prerequisites - A `DAYTONA_API_KEY` environment variable -- A GitHub App configured via `fabro install` (for private repository cloning) +- GitHub access configured via `fabro install` or `gh auth login` (for private repository cloning) ### How it works -- **Sandbox lifecycle** — On `initialize()`, Fabro creates a Daytona sandbox (from an image or a snapshot), clones the current git repository into it, and waits until it's ready. On `cleanup()`, the sandbox is deleted. -- **Working directory** — Fixed at `/home/daytona/workspace`. The current repository is cloned there automatically. -- **Git clone** — Fabro detects the local `origin` remote URL and current branch, converts SSH URLs to HTTPS, and clones into the sandbox. For private repositories, Fabro uses a GitHub App Installation Access Token scoped to `contents: read` on the specific repository. Public repositories are cloned without credentials. If no git repo is detected, the working directory is created empty. +- **Sandbox lifecycle** — On `initialize()`, Fabro creates a Daytona sandbox (from an image or a snapshot), clones the run's GitHub origin into it when one is available, and waits until it's ready. On `cleanup()`, the sandbox is deleted. +- **Working directory** — Fixed at `/home/daytona/workspace`. GitHub-origin runs clone the repository there automatically. +- **Git clone** — Fabro detects the run manifest's GitHub origin URL and branch, converts SSH URLs to HTTPS, and clones into the sandbox. For private repositories, Fabro uses a GitHub App Installation Access Token scoped to the specific repository. Public repositories are cloned without credentials. Set `skip_clone = true` to create an empty workspace instead. - **Commands** — Executed via the Daytona process API. Commands are base64-encoded and piped through `sh` to support pipes, environment variables, and other shell features. - **Ephemeral** — Sandboxes are created with `ephemeral: true` and a unique timestamped name (e.g. `fabro-20260305-142301-a3f2`). @@ -126,6 +141,7 @@ provider = "daytona" [run.sandbox.daytona] auto_stop_interval = 60 +skip_clone = false [run.sandbox.daytona.snapshot] name = "rust-dev" diff --git a/docs/public/execution/run-configuration.mdx b/docs/public/execution/run-configuration.mdx index 00b8e6c78..055ac4054 100644 --- a/docs/public/execution/run-configuration.mdx +++ b/docs/public/execution/run-configuration.mdx @@ -165,10 +165,35 @@ preserve = true | Field | Description | |---|---| -| `provider` | Sandbox mode: `local` (default), `docker`, or `daytona`. | +| `provider` | Sandbox mode: `docker` (default), `local`, or `daytona`. | | `preserve` | When `true`, keep the sandbox alive after the run finishes. Useful for debugging. | | `devcontainer` | When `true`, use the repo's `devcontainer.json` to configure the sandbox. See [Devcontainers](/execution/devcontainers). | +#### `[run.sandbox.docker]` + +Additional settings when using the Docker sandbox: + +```toml title="run.toml" +[run.sandbox] +provider = "docker" + +[run.sandbox.docker] +image = "buildpack-deps:noble" +network_mode = "bridge" +memory_limit = "4GB" +cpu_quota = 200000 +skip_clone = false +``` + +| Field | Description | +|---|---| +| `image` | Docker image used for the run container. Defaults to `buildpack-deps:noble`. | +| `network_mode` | Docker network mode. Defaults to `bridge`. | +| `memory_limit` | Memory limit using human-readable units such as `"4GB"` or `"512MiB"`. | +| `cpu_quota` | Docker CPU quota in microseconds per 100ms period. Defaults to `200000`. | +| `env_vars` | Provider-level environment variables passed to the Docker container. For workflow tool execution, prefer `[run.sandbox.env]`. | +| `skip_clone` | When `true`, start with an empty container workspace instead of cloning the run's GitHub origin. | + #### `[run.sandbox.daytona]` Additional settings when using the Daytona cloud sandbox: @@ -201,6 +226,7 @@ dockerfile = "FROM rust:1.85-slim-bookworm\nRUN apt-get update" | `snapshot.disk` | Disk size using the same units as `memory`. | | `snapshot.dockerfile` | Dockerfile content (inline string) or path (`{ path = "..." }`) for building the snapshot image. Paths are resolved relative to the TOML file's directory. | | `network` | Network access mode: `"allow_all"` (default), `"block"`, or `{ allow_list = ["..."] }`. See [Sandboxing](/administration/sandboxing#network-access-control). | +| `skip_clone` | When `true`, start with an empty Daytona workspace instead of cloning the run's GitHub origin. | #### `[run.sandbox.local]` diff --git a/docs/public/integrations/daytona.mdx b/docs/public/integrations/daytona.mdx index 0d2d42cb0..86c254689 100644 --- a/docs/public/integrations/daytona.mdx +++ b/docs/public/integrations/daytona.mdx @@ -42,6 +42,7 @@ preserve = false [run.sandbox.daytona] auto_stop_interval = 60 +skip_clone = false [run.sandbox.daytona.labels] project = "fabro" @@ -83,7 +84,7 @@ Use `"block"` or a CIDR allow list when running untrusted or generated code to p Snapshots let you pre-build an environment image so each run starts with dependencies already installed rather than installing them in setup commands every time. ```toml title="run.toml" -[sandbox.daytona.snapshot] +[run.sandbox.daytona.snapshot] name = "my-snapshot" cpu = 4 memory = 8 @@ -99,7 +100,17 @@ If a snapshot is configured by name but doesn't exist and no `dockerfile` is pro ## Private repositories -Fabro automatically clones the current repository into the sandbox at `/home/daytona/workspace`. Public repositories work without extra configuration. Private repositories require GitHub access. In `token` mode, Fabro uses the stored token directly. In `app` mode, Fabro uses short-lived Installation Access Tokens scoped to the specific repository. +Fabro automatically clones the run's GitHub origin into the sandbox at `/home/daytona/workspace`. Public repositories work without extra configuration. Private repositories require GitHub access. In `token` mode, Fabro uses the stored token directly. In `app` mode, Fabro uses short-lived Installation Access Tokens scoped to the specific repository. + +Set `skip_clone = true` in `[run.sandbox.daytona]` when a workflow should start with an empty Daytona workspace instead of cloning the run origin: + +```toml title="run.toml" +[run.sandbox] +provider = "daytona" + +[run.sandbox.daytona] +skip_clone = true +``` If the clone fails without GitHub access configured, Fabro suggests running the setup flow: @@ -167,7 +178,7 @@ The `DAYTONA_API_KEY` environment variable is missing or invalid. Store it with ### "Snapshot does not exist and no dockerfile provided" -The run config references a snapshot name that doesn't exist on Daytona, and no `dockerfile` is provided to create it. Either create the snapshot manually in the Daytona dashboard or add a `dockerfile` field to `[sandbox.daytona.snapshot]`. +The run config references a snapshot name that doesn't exist on Daytona, and no `dockerfile` is provided to create it. Either create the snapshot manually in the Daytona dashboard or add a `dockerfile` field to `[run.sandbox.daytona.snapshot]`. ### "Timed out waiting for snapshot to become active" diff --git a/docs/public/reference/dot-language.mdx b/docs/public/reference/dot-language.mdx index b174d6c63..755dd6490 100644 --- a/docs/public/reference/dot-language.mdx +++ b/docs/public/reference/dot-language.mdx @@ -327,7 +327,7 @@ gate -> slow_path Instead of inlining long prompts, reference an external file: ```dot -simplify [label="Simplify", prompt="@files-internal/prompts/simplify.md"] +simplify [label="Simplify", prompt="@prompts/simplify.md"] ``` The `@` prefix tells Fabro to load the prompt from a file path relative to the workflow file. Paths support `~` (home directory) and `..` (parent directory): diff --git a/docs/public/tutorials/branch-loop.mdx b/docs/public/tutorials/branch-loop.mdx index 08d0a58da..215568b59 100644 --- a/docs/public/tutorials/branch-loop.mdx +++ b/docs/public/tutorials/branch-loop.mdx @@ -31,7 +31,7 @@ digraph BranchLoop { ``` ```bash -fabro run files-internal/demo/05-branch-loop.fabro +fabro run docs/internal/demo/05-branch-loop.fabro ``` ## Command nodes diff --git a/docs/public/tutorials/ensemble.mdx b/docs/public/tutorials/ensemble.mdx index 6e4241d38..6f6b87a5f 100644 --- a/docs/public/tutorials/ensemble.mdx +++ b/docs/public/tutorials/ensemble.mdx @@ -52,7 +52,7 @@ digraph Ensemble { ``` ```bash -fabro run files-internal/demo/11-ensemble.fabro +fabro run docs/internal/demo/11-ensemble.fabro ``` diff --git a/docs/public/tutorials/hello-world.mdx b/docs/public/tutorials/hello-world.mdx index aff11e48a..4e913686e 100644 --- a/docs/public/tutorials/hello-world.mdx +++ b/docs/public/tutorials/hello-world.mdx @@ -34,7 +34,7 @@ digraph Hello { Run it: ```bash -fabro run files-internal/demo/01-hello.fabro +fabro run docs/internal/demo/01-hello.fabro ``` ### What's happening @@ -68,7 +68,7 @@ digraph ToolUse { ``` ```bash -fabro run files-internal/demo/02-tool-use.fabro +fabro run docs/internal/demo/02-tool-use.fabro ``` ### What's happening @@ -108,7 +108,7 @@ digraph SubAgent { ``` ```bash -fabro run files-internal/demo/03-subagent.fabro +fabro run docs/internal/demo/03-subagent.fabro ``` ### What's happening diff --git a/docs/public/tutorials/multi-model.mdx b/docs/public/tutorials/multi-model.mdx index de19cd186..9f444ddcb 100644 --- a/docs/public/tutorials/multi-model.mdx +++ b/docs/public/tutorials/multi-model.mdx @@ -36,7 +36,7 @@ digraph MultiModel { ``` ```bash -fabro run files-internal/demo/08-multi-model.fabro +fabro run docs/internal/demo/08-multi-model.fabro ``` ## Model stylesheets diff --git a/docs/public/tutorials/parallel-review.mdx b/docs/public/tutorials/parallel-review.mdx index f7f2f66db..41f589bea 100644 --- a/docs/public/tutorials/parallel-review.mdx +++ b/docs/public/tutorials/parallel-review.mdx @@ -40,7 +40,7 @@ digraph Parallel { ``` ```bash -fabro run files-internal/demo/06-parallel.fabro +fabro run docs/internal/demo/06-parallel.fabro ``` ## Fan-out with the fork node diff --git a/docs/public/tutorials/plan-implement.mdx b/docs/public/tutorials/plan-implement.mdx index 8cd499919..366b41577 100644 --- a/docs/public/tutorials/plan-implement.mdx +++ b/docs/public/tutorials/plan-implement.mdx @@ -22,7 +22,7 @@ digraph PlanImplement { plan [label="Plan", prompt="Analyze the goal and codebase. Write a clear, step-by-step implementation plan to a Markdown file called plan.md. Include what files will change and why.", reasoning_effort="high"] approve [shape=hexagon, label="Approve Plan"] implement [label="Implement", prompt="Read plan.md and implement every step. Make all the code changes described in the plan."] - simplify [label="Simplify", prompt="@files-internal/prompts/simplify.md"] + simplify [label="Simplify", prompt="@prompts/simplify.md"] start -> plan -> approve approve -> implement [label="[A] Approve"] @@ -32,7 +32,7 @@ digraph PlanImplement { ``` ```bash -fabro run files-internal/demo/10-plan-implement.fabro +fabro run docs/internal/demo/10-plan-implement.fabro ``` ## Human gates @@ -77,10 +77,10 @@ The `plan` node sets `reasoning_effort="high"`. This tells the model to think ha ## Prompt file references -The `simplify` node uses `@files-internal/prompts/simplify.md` instead of an inline prompt string: +The `simplify` node uses `@prompts/simplify.md` instead of an inline prompt string: ```dot -simplify [label="Simplify", prompt="@files-internal/prompts/simplify.md"] +simplify [label="Simplify", prompt="@prompts/simplify.md"] ``` The `@` prefix tells Fabro to load the prompt from a Markdown file, resolved relative to the Graphviz file's location. This keeps Graphviz files concise and lets you version prompts as standalone files. See [Prompts](/agents/prompts) for details. diff --git a/docs/public/tutorials/sub-workflow.mdx b/docs/public/tutorials/sub-workflow.mdx index aa4e7c097..48873d2d3 100644 --- a/docs/public/tutorials/sub-workflow.mdx +++ b/docs/public/tutorials/sub-workflow.mdx @@ -62,7 +62,7 @@ digraph SubWorkflow { ``` ```bash -fabro run files-internal/demo/12-sub-workflow.fabro +fabro run docs/internal/demo/12-sub-workflow.fabro ``` ## The house node