mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-12 23:02:41 +00:00
Add Node Outcomes docs page and fix status gaps across docs
New page (execution/outcomes.mdx) defines the 5 stage statuses, documents how each handler produces them, and explains allow_partial, auto_status, the retry loop, goal gate interaction, and outcome in edge conditions. Existing pages updated: added missing `skipped` status to outcome key descriptions, improved `goal_gate`/`auto_status` descriptions in the dot-language reference, added `allow_partial` to the attributes table, and added cross-links from failures.mdx and transitions.mdx. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f4e9503df1
commit
2172f5987e
6 changed files with 140 additions and 8 deletions
|
|
@ -41,7 +41,7 @@ The engine walks the graph starting from the start node. For each node it:
|
|||
|
||||
1. **Resolves context** — Assembles the node's input from prior stage outputs, run context, and the workflow goal. The [fidelity](/workflows/stages-and-nodes#agent) setting controls how much prior context is included.
|
||||
2. **Dispatches to a handler** — Each [node type](/workflows/stages-and-nodes) has a handler: the agent handler runs an LLM tool loop, the command handler runs a shell script, the human handler waits for input, and so on.
|
||||
3. **Collects the outcome** — The handler returns a status (`success`, `fail`, `partial_success`), optional routing directives, and any context updates.
|
||||
3. **Collects the outcome** — The handler returns a [status](/execution/outcomes) (`success`, `fail`, `partial_success`, `retry`, or `skipped`), optional routing directives, and any context updates.
|
||||
4. **Selects the next edge** — Fabro evaluates outgoing edges using conditions, labels, and weights to pick the next node. See [Transitions](/workflows/transitions).
|
||||
5. **Checkpoints** — After each stage, Fabro writes a checkpoint so the run can be resumed if interrupted.
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
"execution/environments",
|
||||
"execution/context",
|
||||
"execution/checkpoints",
|
||||
"execution/outcomes",
|
||||
"execution/failures",
|
||||
"execution/retros",
|
||||
"execution/observability",
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ The engine resolves retry configuration in this order:
|
|||
|
||||
Not all errors trigger a node retry. The handler's `should_retry` check must return true — generally, only errors classified as transient are retried. Deterministic errors (auth failures, bad config) fail immediately without consuming retry attempts.
|
||||
|
||||
When a handler returns a `Retry` status instead of `Fail`, retries always proceed (if attempts remain). If retries are exhausted and the node has `allow_partial=true`, the outcome is promoted to `PartialSuccess` instead of failing.
|
||||
When a handler returns a `Retry` status instead of `Fail`, retries always proceed (if attempts remain). If retries are exhausted and the node has `allow_partial=true`, the outcome is promoted to `PartialSuccess` instead of failing. See [Node Outcomes — Retry loop](/execution/outcomes#retry-loop) for a detailed flow diagram.
|
||||
|
||||
## Model fallbacks
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ Loop restart edges also have their own separate circuit breaker (`restart_failur
|
|||
|
||||
## Goal gates
|
||||
|
||||
Goal gates are quality checkpoints that are enforced when the workflow reaches an exit node. A node marked with `goal_gate=true` must have completed with `success` or `partial_success` — otherwise the run cannot finish.
|
||||
Goal gates are quality checkpoints that are enforced when the workflow reaches an exit node. A node marked with `goal_gate=true` must have completed with `success` or `partial_success` — otherwise the run cannot finish. See [Node Outcomes — Goal gate interaction](/execution/outcomes#goal-gate-interaction) for how `partial_success` and `allow_partial` interact with goal gates.
|
||||
|
||||
```dot
|
||||
verify [shape=box, goal_gate="true"]
|
||||
|
|
|
|||
130
docs/execution/outcomes.mdx
Normal file
130
docs/execution/outcomes.mdx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
title: "Node Outcomes"
|
||||
description: "The five stage statuses and the attributes that control them"
|
||||
---
|
||||
|
||||
Every node execution produces an **outcome** containing a status that drives edge routing, retry logic, and goal gate checks. This page defines the five statuses and the attributes that influence them.
|
||||
|
||||
## The five statuses
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `success` | The handler completed normally |
|
||||
| `fail` | The handler encountered an unrecoverable error |
|
||||
| `partial_success` | The handler did not fully succeed but produced usable results — typically from retries exhausted with `allow_partial=true` |
|
||||
| `retry` | The handler wants to re-execute — consumed by the [retry loop](#retry-loop) and never appears in edge conditions |
|
||||
| `skipped` | The node was not executed (e.g. a branch not taken in a parallel fan-out) |
|
||||
|
||||
<Note>
|
||||
`retry` is internal to the engine. It triggers re-execution inside the retry loop and is never visible in edge `condition` expressions. The four externally-visible statuses are `success`, `fail`, `partial_success`, and `skipped`.
|
||||
</Note>
|
||||
|
||||
## How handlers produce statuses
|
||||
|
||||
Each node type has its own rules for which statuses it can return:
|
||||
|
||||
| Handler | Produces | Conditions |
|
||||
|---|---|---|
|
||||
| **Command** | `success`, `fail` | `success` when exit code is 0; `fail` otherwise |
|
||||
| **Agent / Prompt** | `success`, `fail`, `partial_success`, `retry`, `skipped` | Defaults to `success`. The LLM can set any status via a [routing directive](/agents/outputs#routing-directives) JSON object in its response. Backend errors produce `retry` (if retryable) or `fail`. |
|
||||
| **Parallel** | `success`, `partial_success`, `fail` | Depends on the `join_policy`. `wait_all`: `success` if no failures, `partial_success` if some branches failed. `first_success` / `k_of_n` / `quorum`: `success` if threshold met, else `fail`. |
|
||||
| **Human** | `success` | Always succeeds — the user's selection becomes a routing signal via `preferred_label` |
|
||||
| **Conditional** | `success` | Always succeeds — routing is handled by the engine's edge selection |
|
||||
| **Start / Exit / Wait** | `success` | Always succeed |
|
||||
|
||||
## Retry loop
|
||||
|
||||
When a handler returns `retry` status, the engine enters the retry loop. If retry attempts remain (per the node's [retry policy](/execution/failures#retry-policies)), the handler re-executes after a backoff delay. If attempts are exhausted, the final status depends on `allow_partial`:
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Run handler │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┐ success/fail/
|
||||
│ Status? │───partial_success/───▶ Done (use as-is)
|
||||
└────┬─────┘ skipped
|
||||
│ retry
|
||||
▼
|
||||
┌──────────────┐ yes ┌──────────────┐
|
||||
│ Attempts │─────────▶│ Backoff + │──┐
|
||||
│ remain? │ │ re-execute │ │
|
||||
└──────┬───────┘ └──────────────┘ │
|
||||
│ no │
|
||||
▼ ┌──────────┘
|
||||
┌──────────────┐ │
|
||||
│allow_partial?│ │ (loops back to
|
||||
└──────┬───┬───┘ │ "Run handler")
|
||||
yes │ │ no │
|
||||
▼ ▼
|
||||
partial_ fail
|
||||
success
|
||||
```
|
||||
|
||||
Handler errors follow the same loop: retryable errors (transient infrastructure) re-execute if attempts remain; non-retryable errors (authentication, bad config) fail immediately without consuming retry attempts.
|
||||
|
||||
## `allow_partial`
|
||||
|
||||
When `allow_partial=true` and the retry loop exhausts all attempts on a `retry` status, the outcome is promoted to `partial_success` instead of `fail`. This lets the workflow continue past nodes that could not fully succeed.
|
||||
|
||||
| Attribute | Type | Default |
|
||||
|---|---|---|
|
||||
| `allow_partial` | Boolean | `false` |
|
||||
|
||||
```dot
|
||||
implement [
|
||||
label="Implement",
|
||||
retry_policy="standard",
|
||||
allow_partial=true,
|
||||
prompt="Implement the feature."
|
||||
]
|
||||
```
|
||||
|
||||
In this example, if the agent returns `retry` and all 5 standard-policy attempts are used, the node finishes with `partial_success` rather than failing the run.
|
||||
|
||||
See [Retry policies](/execution/failures#retry-policies) for the available presets and backoff settings.
|
||||
|
||||
## `auto_status`
|
||||
|
||||
When `auto_status=true`, any non-`success` and non-`skipped` status is silently overridden to `success` after the handler completes. This is applied after the retry loop, so retries still happen normally — only the final outcome is overridden.
|
||||
|
||||
| Attribute | Type | Default |
|
||||
|---|---|---|
|
||||
| `auto_status` | Boolean | `false` |
|
||||
|
||||
```dot
|
||||
scan [
|
||||
label="Scan",
|
||||
shape=parallelogram,
|
||||
auto_status=true,
|
||||
script="find . -name '*.log' | head -20"
|
||||
]
|
||||
```
|
||||
|
||||
Use `auto_status` for nodes whose failure should never block the workflow — optional scans, best-effort cleanup steps, or informational commands where the output matters more than the exit code.
|
||||
|
||||
## Goal gate interaction
|
||||
|
||||
Nodes marked with `goal_gate=true` are checked when the workflow reaches the exit node. A goal gate is satisfied if its last outcome was `success` **or** `partial_success`. Any other status (`fail`, `skipped`) causes the workflow to fail, even though execution reached the exit.
|
||||
|
||||
This means `allow_partial=true` on a goal gate node lets the gate pass even if the node exhausted its retries — the promoted `partial_success` counts as passing.
|
||||
|
||||
See [Goal gates](/execution/failures#goal-gates) for retry target resolution and failure behavior.
|
||||
|
||||
## Outcome in edge conditions
|
||||
|
||||
The four externally-visible statuses can be used in edge `condition` expressions via the `outcome` key:
|
||||
|
||||
```dot
|
||||
gate -> deploy [label="Pass", condition="outcome=success"]
|
||||
gate -> fix [label="Fix", condition="outcome=fail"]
|
||||
gate -> review [label="Partial", condition="outcome=partial_success"]
|
||||
gate -> skip [label="Skipped", condition="outcome=skipped"]
|
||||
|
||||
// Common pattern: treat partial as passing
|
||||
gate -> deploy [condition="outcome=success || outcome=partial_success"]
|
||||
gate -> fix [condition="outcome=fail"]
|
||||
```
|
||||
|
||||
See [Transitions](/workflows/transitions) for the full edge selection logic and operator reference.
|
||||
|
|
@ -187,8 +187,9 @@ Start nodes can also be identified by ID (`start` or `Start`). Exit nodes can be
|
|||
| `max_retries` | Integer | Override default retry count |
|
||||
| `retry_policy` | String | Named preset: `none`, `standard`, `aggressive`, `linear`, `patient` |
|
||||
| `retry_target` | String | Node ID to jump to on retry |
|
||||
| `goal_gate` | Boolean | When `true`, workflow fails if this node doesn't succeed |
|
||||
| `auto_status` | Boolean | Auto-generate status updates |
|
||||
| `goal_gate` | Boolean | When `true`, workflow fails if this node didn't finish with `success` or `partial_success`. See [Node Outcomes](/execution/outcomes#goal-gate-interaction). |
|
||||
| `auto_status` | Boolean | When `true`, overrides any non-`success`/non-`skipped` status to `success` after the handler completes. See [Node Outcomes](/execution/outcomes#auto_status). |
|
||||
| `allow_partial` | Boolean | When `true` and retries are exhausted on a `retry` status, promotes the outcome to `partial_success` instead of `fail`. Default `false`. See [Node Outcomes](/execution/outcomes#allow_partial). |
|
||||
| `selection` | String | Edge tiebreaking strategy: `deterministic` (default) or `random` (weighted-random). Cannot be combined with conditional edges. |
|
||||
|
||||
### Agent and prompt nodes
|
||||
|
|
@ -257,7 +258,7 @@ Op ::= '=' | '!=' | '>' | '<' | '>=' | '<='
|
|||
|
||||
| Key | Resolves to |
|
||||
|---|---|
|
||||
| `outcome` | Stage status: `success`, `fail`, or `partial_success` |
|
||||
| `outcome` | Stage status: `success`, `fail`, `partial_success`, or `skipped`. See [Node Outcomes](/execution/outcomes#outcome-in-edge-conditions). |
|
||||
| `preferred_label` | Label selected by a human gate or LLM routing directive |
|
||||
| `context.KEY` | Value from the run context |
|
||||
| `KEY` | Shorthand for context lookup (without the `context.` prefix) |
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ After each node finishes, Fabro must decide which edge to follow to the next nod
|
|||
|
||||
## How transitions work
|
||||
|
||||
When a node completes, it produces an **outcome** with a status (`success`, `fail`, `partial_success`) and optional signals like a preferred label or suggested next node. Fabro evaluates the outgoing edges in a fixed priority order:
|
||||
When a node completes, it produces an **outcome** with a [status](/execution/outcomes) (`success`, `fail`, `partial_success`, or `skipped`) and optional signals like a preferred label or suggested next node. Fabro evaluates the outgoing edges in a fixed priority order:
|
||||
|
||||
1. **Condition match** — Edges with a `condition` attribute are evaluated first. If one or more conditions match, the edge with the highest `weight` wins (lexical tiebreak on target node ID).
|
||||
2. **Preferred label** — If the node's outcome includes a preferred label (e.g. from a human gate selection), the edge whose `label` matches is chosen.
|
||||
|
|
@ -37,7 +37,7 @@ gate -> implement [label="Fix", condition="outcome=fail"]
|
|||
|
||||
| Key | Resolves to |
|
||||
|---|---|
|
||||
| `outcome` | The stage status: `success`, `fail`, or `partial_success` |
|
||||
| `outcome` | The stage status: `success`, `fail`, `partial_success`, or `skipped`. See [Node Outcomes](/execution/outcomes). |
|
||||
| `preferred_label` | The label selected by a human gate |
|
||||
| `context.KEY` | A value from the run context (e.g. `context.tests_passed`) |
|
||||
| `KEY` | Shorthand for context lookup (without the `context.` prefix) |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue