diff --git a/apps/fabro-web/app/routes/run-files/states.tsx b/apps/fabro-web/app/routes/run-files/states.tsx
index 0ca7fa5ab..026694269 100644
--- a/apps/fabro-web/app/routes/run-files/states.tsx
+++ b/apps/fabro-web/app/routes/run-files/states.tsx
@@ -51,7 +51,7 @@ export function emptyStateCopy(kind: EmptyKind): string {
/// The full RunStatus enum (per fabro-types/src/status.rs) is:
/// submitted, queued, starting, running, blocked, paused, removing,
/// succeeded, failed, dead, archived
-/// partial_success is a stage status, not a run status.
+/// partially_succeeded is a stage outcome, not a run status.
export function deriveEmptyKind(args: {
runStatus: string | undefined;
totalChanged: number;
diff --git a/docs/internal/cli-workflow-coupling-audit.md b/docs/internal/cli-workflow-coupling-audit.md
index b7070c727..7fdf953ba 100644
--- a/docs/internal/cli-workflow-coupling-audit.md
+++ b/docs/internal/cli-workflow-coupling-audit.md
@@ -16,16 +16,16 @@
| Path | Direct dependency | Why it still exists | Required remediation track |
| --- | --- | --- | --- |
-| `lib/crates/fabro-cli/src/commands/pr/create.rs` | `outcome::StageStatus`, `pull_request::maybe_open_pull_request` | CLI still reconstructs store state and runs PR creation logic from the workflow pipeline directly. | Replace with a server API, or extract PR orchestration into a non-engine shared service crate plus API. |
+| `lib/crates/fabro-cli/src/commands/pr/create.rs` | `StageOutcome`, `pull_request::maybe_open_pull_request` | CLI still reconstructs store state and runs PR creation logic from the workflow pipeline directly. | Replace with a server API, or extract PR orchestration into a non-engine shared service crate plus API. |
| `lib/crates/fabro-cli/src/commands/run/runner.rs` | `artifact_snapshot::CapturedArtifactInfo`, `artifact_upload::{ArtifactSink, StageArtifactUploader}`, `event::{Emitter, RunEventSink}`, `operations::{self, StartServices}`, `run_control::RunControlState`, `runtime_store::{RunStoreBackend, RunStoreHandle}` | Hidden worker subprocess path still lives inside the CLI crate and embeds the workflow engine directly. | Re-home worker/runtime code outside the user CLI surface, ideally into a dedicated worker crate or binary. |
| `lib/crates/fabro-cli/src/manifest_builder.rs` | `git::{GitSyncStatus, head_sha, sync_status}` | Manifest submission still relies on git helper logic that happens to live in `fabro_workflow`. | Extract git-sync inspection helpers into a non-workflow shared crate/module. |
| `lib/crates/fabro-cli/src/server_client.rs` | `artifact_snapshot::CapturedArtifactInfo` | The upload client reuses a workflow-owned artifact snapshot DTO. | Extract shared artifact snapshot DTOs into `fabro-store`, `fabro-types`, or a dedicated shared crate. |
| `lib/crates/fabro-cli/src/commands/runs/inspect.rs` | `run_status::RunStatus` | CLI output types still depend on engine-owned run status enums. | Extract shared status types into `fabro-types` or switch to API-generated/public store types. |
| `lib/crates/fabro-cli/src/commands/runs/list.rs` | `run_status::RunStatus` | List rendering still depends on engine-owned run status enums. | Extract shared status types into `fabro-types` or switch to API-generated/public store types. |
-| `lib/crates/fabro-cli/src/commands/run/attach.rs` | `outcome::StageStatus`, `run_status::RunStatus` | Attach/replay logic still formats engine-owned terminal status types directly. | Extract shared run/conclusion status types into `fabro-types`. |
-| `lib/crates/fabro-cli/src/commands/run/output.rs` | `outcome::StageStatus`, `records::Conclusion` | Human-readable completion output still consumes workflow-owned conclusion/status records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. |
+| `lib/crates/fabro-cli/src/commands/run/attach.rs` | `StageOutcome`, `run_status::RunStatus` | Attach/replay logic still formats engine-owned terminal status types directly. | Extract shared run/conclusion status types into `fabro-types`. |
+| `lib/crates/fabro-cli/src/commands/run/output.rs` | `StageOutcome`, `records::Conclusion` | Human-readable completion output still consumes workflow-owned conclusion/status records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. |
| `lib/crates/fabro-cli/src/commands/run/wait.rs` | `records::Conclusion`, `run_status::RunStatus` | Wait output still depends on workflow-owned status/conclusion records. | Extract shared conclusion/status DTOs into `fabro-types` or `fabro-store`. |
-| `lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs` | `outcome::{StageStatus, format_cost}` | Progress UI still depends on workflow-owned stage status and cost-formatting helper code. | Extract shared stage status types into `fabro-types` and move formatting helpers into `fabro-util`. |
+| `lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs` | `StageOutcome`, `format_cost` | Progress UI still depends on shared stage outcome and workflow-owned cost-formatting helper code. | Move formatting helpers into `fabro-util`. |
| `lib/crates/fabro-cli/src/commands/run/run_progress/info_display.rs` | `event::RunNoticeLevel` | Progress UI still formats workflow-owned notice levels directly. | Extract shared notice/event enums into `fabro-types`. |
| `lib/crates/fabro-cli/src/commands/run/run_progress/event.rs` | `event::RunNoticeLevel` | Progress event translation still depends on workflow-owned notice levels. | Extract shared notice/event enums into `fabro-types`. |
@@ -34,7 +34,7 @@
| Path | Direct dependency | Why it still exists | Suggested handling |
| --- | --- | --- | --- |
| `lib/crates/fabro-cli/src/commands/dump.rs` test module | `event::{Event, append_event}` | Unit tests synthesize workflow events directly. | Low priority; keep until a lighter-weight event fixture helper exists. |
-| `lib/crates/fabro-cli/src/commands/run/wait.rs` test module | `outcome::StageStatus`, `records::Conclusion`, `run_status::RunStatusRecord` | Output tests construct workflow-owned records directly. | Replace with shared fixture builders once status/conclusion DTOs move out. |
+| `lib/crates/fabro-cli/src/commands/run/wait.rs` test module | `StageOutcome`, `records::Conclusion`, `run_status::RunStatusRecord` | Output tests construct workflow-owned records directly. | Replace with shared fixture builders once conclusion DTOs move out. |
| `lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs` test module | `event::{Event, RunNoticeLevel, to_run_event, to_run_event_at}`, `outcome::billed_model_usage_from_llm` | Progress tests build engine events directly. | Replace with shared event fixture helpers after event DTO extraction. |
| `lib/crates/fabro-cli/src/commands/run/run_progress/event.rs` test module | `event::{Event, to_run_event}` | Event rendering tests depend on engine event constructors. | Replace with shared event fixture helpers after event DTO extraction. |
| `lib/crates/fabro-cli/src/commands/run/runner.rs` test module | `artifact_upload::StageArtifactUploader` | Worker tests still reach into workflow upload internals. | Keep with worker re-home work; not worth separating first. |
diff --git a/docs/internal/events.md b/docs/internal/events.md
index 446657fed..81d0a0a96 100644
--- a/docs/internal/events.md
+++ b/docs/internal/events.md
@@ -123,7 +123,7 @@ Emitted when the workflow run finishes successfully (or with partial success).
"properties": {
"duration_ms": 45000,
"artifact_count": 3,
- "status": "success",
+ "status": "succeeded",
"total_cost": 0.15,
"final_git_commit_sha": "def456...",
"usage": {
@@ -143,7 +143,7 @@ Emitted when the workflow run finishes successfully (or with partial success).
|----------|------|-------------|
| `duration_ms` | number | Total run duration in milliseconds |
| `artifact_count` | number | Number of artifacts produced |
-| `status` | string | Final status (`"success"`, `"fail"`, `"partial_success"`) |
+| `status` | string | Final stage outcome (`"succeeded"`, `"failed"`, `"partially_succeeded"`, `"skipped"`) |
| `total_cost` | number? | Aggregate cost in USD |
| `final_git_commit_sha` | string? | Final HEAD SHA |
| `usage` | object? | Aggregate token usage |
@@ -327,7 +327,7 @@ Emitted when a workflow node finishes execution.
"properties": {
"index": 1,
"duration_ms": 8000,
- "status": "success",
+ "status": "succeeded",
"preferred_label": "tests_pass",
"suggested_next_ids": ["review"],
"usage": {
@@ -362,7 +362,7 @@ Emitted when a workflow node finishes execution.
|----------|------|-------------|
| `index` | number | Stage execution order index |
| `duration_ms` | number | Stage duration in milliseconds |
-| `status` | string | `"success"`, `"fail"`, `"skipped"`, `"partial_success"`, `"retry"` |
+| `status` | string | `"succeeded"`, `"failed"`, `"skipped"`, `"partially_succeeded"` |
| `preferred_label` | string? | Edge label hint for routing |
| `suggested_next_ids` | string[] | Suggested successor node ids |
| `usage` | object? | Token usage for this stage |
@@ -522,7 +522,7 @@ Emitted when a parallel branch finishes.
"properties": {
"index": 0,
"duration_ms": 5000,
- "status": "success"
+ "status": "succeeded"
}
}
```
@@ -640,7 +640,7 @@ Emitted after a checkpoint is saved.
"node_id": "code",
"node_label": "code",
"properties": {
- "status": "success",
+ "status": "succeeded",
"git_commit_sha": "abc123...",
"diff": "diff --git a/src/lib.rs b/src/lib.rs\n..."
}
@@ -821,7 +821,7 @@ Emitted when the engine selects the next edge to traverse.
"reason": "condition",
"preferred_label": "tests_pass",
"suggested_next_ids": ["review"],
- "stage_status": "success",
+ "stage_status": "succeeded",
"is_jump": false
}
}
@@ -1490,7 +1490,7 @@ Emitted when the agent fails over to a different LLM provider/model.
"node_label": "pipeline",
"properties": {
"steps_executed": 4,
- "status": "success",
+ "status": "succeeded",
"duration_ms": 25000
}
}
diff --git a/docs/public/agents/outputs.mdx b/docs/public/agents/outputs.mdx
index 669bcf43b..a15d9a9ff 100644
--- a/docs/public/agents/outputs.mdx
+++ b/docs/public/agents/outputs.mdx
@@ -45,8 +45,8 @@ Agent and prompt nodes can influence which edge is taken after they complete by
| Field | Effect |
|---|---|
-| `outcome` | Sets the node status: `success`, `fail`, `partial_success`, `retry`, or `skipped` |
-| `failure_reason` | When `outcome` is `fail`, provides a structured failure message |
+| `outcome` | Sets the node outcome: `succeeded`, `failed`, `partially_succeeded`, or `skipped` |
+| `failure_reason` | When `outcome` is `failed`, provides a structured failure message |
| `preferred_next_label` | Matched against edge labels to select the next node |
| `suggested_next_ids` | Ordered list of preferred target node IDs |
| `context_updates` | Key-value pairs merged into the run context |
@@ -110,7 +110,7 @@ The tracked paths are stored as `files_touched` on the stage outcome:
```json
{
- "status": "success",
+ "status": "succeeded",
"files_touched": ["src/main.rs", "tests/api_test.rs", "README.md"]
}
```
diff --git a/docs/public/api-reference/fabro-api.yaml b/docs/public/api-reference/fabro-api.yaml
index 6cddb40ef..be3b8db8c 100644
--- a/docs/public/api-reference/fabro-api.yaml
+++ b/docs/public/api-reference/fabro-api.yaml
@@ -5635,7 +5635,7 @@ components:
properties:
force:
type: boolean
- description: Create the pull request even if the run did not finish with success or partial_success.
+ description: Create the pull request even if the run did not finish with succeeded or partially_succeeded.
example: false
model:
type: ["string", "null"]
diff --git a/docs/public/core-concepts/how-fabro-works.mdx b/docs/public/core-concepts/how-fabro-works.mdx
index 70a68a7d7..288e7796a 100644
--- a/docs/public/core-concepts/how-fabro-works.mdx
+++ b/docs/public/core-concepts/how-fabro-works.mdx
@@ -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](/execution/outcomes) (`success`, `fail`, `partial_success`, `retry`, or `skipped`), optional routing directives, and any context updates.
+3. **Collects the outcome** — The handler returns a [stage outcome](/execution/outcomes) (`succeeded`, `failed`, `partially_succeeded`, 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.
diff --git a/docs/public/execution/context.mdx b/docs/public/execution/context.mdx
index 9dce787bf..8fa5ac2c4 100644
--- a/docs/public/execution/context.mdx
+++ b/docs/public/execution/context.mdx
@@ -75,7 +75,7 @@ The engine sets several keys automatically. These are prefixed with `internal.`
| `internal.thread_id` | Thread ID for shared-conversation nodes (or null) |
| `internal.node_visit_count` | How many times the current node has been visited |
| `internal.retry_count.{node_id}` | Number of retry attempts used by a node |
-| `outcome` | Status of the last completed stage (`success`, `fail`, etc.) |
+| `outcome` | Status of the last completed stage (`succeeded`, `failed`, etc.) |
| `failure_class` | Classification of the last failure (if any) |
| `failure_signature` | Deduplication signature for the last failure |
| `preferred_label` | Label selected by a human gate or agent routing directive |
diff --git a/docs/public/execution/failures.mdx b/docs/public/execution/failures.mdx
index d1cf99444..6884d5708 100644
--- a/docs/public/execution/failures.mdx
+++ b/docs/public/execution/failures.mdx
@@ -108,7 +108,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. See [Node Outcomes — Retry loop](/execution/outcomes#retry-loop) for a detailed flow diagram.
+When a handler reports a retryable failure, retries always proceed if attempts remain. If retries are exhausted and the node has `allow_partial=true`, the outcome is promoted to `partially_succeeded` instead of `failed`. See [Node Outcomes — Retry loop](/execution/outcomes#retry-loop) for a detailed flow diagram.
## Model fallbacks
@@ -234,7 +234,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. See [Node Outcomes — Goal gate interaction](/execution/outcomes#goal-gate-interaction) for how `partial_success` and `allow_partial` interact with 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 `succeeded` or `partially_succeeded` — otherwise the run cannot finish. See [Node Outcomes — Goal gate interaction](/execution/outcomes#goal-gate-interaction) for how `partially_succeeded` and `allow_partial` interact with goal gates.
```dot
verify [shape=box, goal_gate="true"]
diff --git a/docs/public/execution/outcomes.mdx b/docs/public/execution/outcomes.mdx
index 80357ec98..efc8a8688 100644
--- a/docs/public/execution/outcomes.mdx
+++ b/docs/public/execution/outcomes.mdx
@@ -1,40 +1,39 @@
---
title: "Node Outcomes"
-description: "The five stage statuses and the attributes that control them"
+description: "The four stage outcomes 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.
+Every node execution produces an **outcome** that drives edge routing, retry logic, and goal gate checks. This page defines the four externally visible outcomes and the attributes that influence them.
-## The five statuses
+## The four outcomes
-| Status | Meaning |
+| Outcome | 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 |
+| `succeeded` | The handler completed normally |
+| `failed` | The handler encountered an unrecoverable error |
+| `partially_succeeded` | The handler did not fully succeed but produced usable results — typically from retries exhausted with `allow_partial=true` |
| `skipped` | The node was not executed (e.g. a branch not taken in a parallel fan-out) |
-`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`.
+Retry intent is internal to the engine. It triggers re-execution inside the retry loop and is never visible in edge `condition` expressions. Retryable failures emit `stage.retrying` events while the node is still active, then finish as one of the four outcomes above.
-## How handlers produce statuses
+## How handlers produce outcomes
-Each node type has its own rules for which statuses it can return:
+Each node type has its own rules for which outcomes 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`: `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 |
+| **Command** | `succeeded`, `failed` | `succeeded` when exit code is 0; `failed` otherwise |
+| **Agent / Prompt** | `succeeded`, `failed`, `partially_succeeded`, `skipped` | Defaults to `succeeded`. The LLM can set any outcome via a [routing directive](/agents/outputs#routing-directives) JSON object in its response. Backend errors request retry when retryable or finish as `failed`. |
+| **Parallel** | `succeeded`, `partially_succeeded`, `failed` | Depends on the `join_policy`. `wait_all`: `succeeded` if no failures, `partially_succeeded` if some branches failed. `first_success`: `succeeded` if threshold met, else `failed`. |
+| **Human** | `succeeded` | Always succeeds — the user's selection becomes a routing signal via `preferred_label` |
+| **Conditional** | `succeeded` | Always succeeds — routing is handled by the engine's edge selection |
+| **Start / Exit / Wait** | `succeeded` | 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`:
+When a handler returns a retryable failure, 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 outcome depends on `allow_partial`:
```
┌─────────────┐
@@ -42,10 +41,10 @@ When a handler returns `retry` status, the engine enters the retry loop. If retr
└──────┬──────┘
│
▼
- ┌──────────┐ success/fail/
- │ Status? │───partial_success/───▶ Done (use as-is)
- └────┬─────┘ skipped
- │ retry
+ ┌──────────┐ succeeded / failed /
+ │Outcome? │─── partially_succeeded / skipped ──▶ Done (use as-is)
+ └────┬─────┘
+ │ retryable failure
▼
┌──────────────┐ yes ┌──────────────┐
│ Attempts │─────────▶│ Backoff + │──┐
@@ -58,15 +57,14 @@ When a handler returns `retry` status, the engine enters the retry loop. If retr
└──────┬───┬───┘ │ "Run handler")
yes │ │ no │
▼ ▼
- partial_ fail
- success
+ partially_succeeded failed
```
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.
+When `allow_partial=true` and the retry loop exhausts all attempts on a retryable failure, the outcome is promoted to `partially_succeeded` instead of `failed`. This lets the workflow continue past nodes that could not fully succeed.
| Attribute | Type | Default |
|---|---|---|
@@ -81,13 +79,13 @@ implement [
]
```
-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.
+In this example, if the agent returns a retryable failure and all 5 standard-policy attempts are used, the node finishes with `partially_succeeded` 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.
+When `auto_status=true`, any non-`succeeded` and non-`skipped` outcome is silently overridden to `succeeded` 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 |
|---|---|---|
@@ -106,9 +104,9 @@ Use `auto_status` for nodes whose failure should never block the workflow — op
## 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.
+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 `succeeded` **or** `partially_succeeded`. Any other outcome (`failed`, `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.
+This means `allow_partial=true` on a goal gate node lets the gate pass even if the node exhausted its retries — the promoted `partially_succeeded` outcome counts as passing.
See [Goal gates](/execution/failures#goal-gates) for retry target resolution and failure behavior.
diff --git a/docs/public/reference/cli.mdx b/docs/public/reference/cli.mdx
index aa290b8ba..8ce0d8580 100644
--- a/docs/public/reference/cli.mdx
+++ b/docs/public/reference/cli.mdx
@@ -591,7 +591,7 @@ fabro pr create [OPTIONS]
| Option | Description |
| --- | --- |
-| `-f, --force` | Create PR even if the run status is not success/partial_success |
+| `-f, --force` | Create PR even if the run status is not succeeded/partially_succeeded |
| `--model ` | LLM model for generating PR description |
| `--server ` | Fabro server target: http(s) URL or absolute Unix socket path |
diff --git a/docs/public/reference/dot-language.mdx b/docs/public/reference/dot-language.mdx
index 8c41b0343..b174d6c63 100644
--- a/docs/public/reference/dot-language.mdx
+++ b/docs/public/reference/dot-language.mdx
@@ -189,9 +189,9 @@ Start nodes can also be identified by ID (`start` or `Start`). Exit nodes can be
| `retry_policy` | String | Named preset: `none`, `standard`, `aggressive`, `linear`, `patient` |
| `retry_target` | String | Node ID to jump to on retry |
| `fallback_retry_target` | String | Fallback node ID if primary `retry_target` is unreachable |
-| `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). |
+| `goal_gate` | Boolean | When `true`, workflow fails if this node didn't finish with `succeeded` or `partially_succeeded`. See [Node Outcomes](/execution/outcomes#goal-gate-interaction). |
+| `auto_status` | Boolean | When `true`, overrides any non-`succeeded`/non-`skipped` outcome to `succeeded` after the handler completes. See [Node Outcomes](/execution/outcomes#auto_status). |
+| `allow_partial` | Boolean | When `true` and retries are exhausted on a retry-requesting failure, promotes the outcome to `partially_succeeded` instead of `failed`. 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
@@ -271,7 +271,7 @@ Op ::= '=' | '!=' | '>' | '<' | '>=' | '<='
| Key | Resolves to |
|---|---|
-| `outcome` | Stage status: `success`, `fail`, `partial_success`, or `skipped`. See [Node Outcomes](/execution/outcomes#outcome-in-edge-conditions). |
+| `outcome` | Stage outcome: `succeeded`, `failed`, `partially_succeeded`, 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) |
diff --git a/docs/public/workflows/transitions.mdx b/docs/public/workflows/transitions.mdx
index e2451599a..20744e9cf 100644
--- a/docs/public/workflows/transitions.mdx
+++ b/docs/public/workflows/transitions.mdx
@@ -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](/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:
+When a node completes, it produces an **outcome** with a [stage outcome](/execution/outcomes) (`succeeded`, `failed`, `partially_succeeded`, 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=failed"]
| Key | Resolves to |
|---|---|
-| `outcome` | The stage status: `success`, `fail`, `partial_success`, or `skipped`. See [Node Outcomes](/execution/outcomes). |
+| `outcome` | The stage outcome: `succeeded`, `failed`, `partially_succeeded`, 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) |
diff --git a/lib/crates/fabro-cli/src/args.rs b/lib/crates/fabro-cli/src/args.rs
index e321d4679..52ea3a87a 100644
--- a/lib/crates/fabro-cli/src/args.rs
+++ b/lib/crates/fabro-cli/src/args.rs
@@ -767,7 +767,7 @@ pub(crate) struct PrCreateArgs {
/// LLM model for generating PR description
#[arg(long)]
pub(crate) model: Option,
- /// Create PR even if the run status is not success/partial_success
+ /// Create PR even if the run status is not succeeded/partially_succeeded
#[arg(short, long)]
pub(crate) force: bool,
}
diff --git a/lib/crates/fabro-cli/src/commands/run/attach.rs b/lib/crates/fabro-cli/src/commands/run/attach.rs
index 5cfb23bda..4e71d6a28 100644
--- a/lib/crates/fabro-cli/src/commands/run/attach.rs
+++ b/lib/crates/fabro-cli/src/commands/run/attach.rs
@@ -39,7 +39,7 @@ const ATTACH_PREMATURE_EOF_MESSAGE: &str = "Attach stream ended before terminal
/// Attach to a running (or finished) workflow run, rendering progress live.
///
-/// Returns exit code 0 for success/partial_success, 1 otherwise.
+/// Returns exit code 0 for succeeded/partially_succeeded, 1 otherwise.
#[cfg(test)]
pub(crate) async fn attach_run(
run_dir: &Path,
@@ -455,7 +455,7 @@ fn state_exit_code(state: &server_client::RunProjection) -> Option {
fn event_exit_code(event: &EventEnvelope) -> Option {
match &event.event.body {
EventBody::RunCompleted(props) => Some(
- if props.status == "success" || props.status == "partial_success" {
+ if props.status == "succeeded" || props.status == "partially_succeeded" {
ExitCode::from(0)
} else {
ExitCode::from(1)
diff --git a/lib/crates/fabro-cli/src/commands/run/logs.rs b/lib/crates/fabro-cli/src/commands/run/logs.rs
index b5cde4456..7ff438998 100644
--- a/lib/crates/fabro-cli/src/commands/run/logs.rs
+++ b/lib/crates/fabro-cli/src/commands/run/logs.rs
@@ -374,11 +374,11 @@ fn format_event_pretty_value(envelope: &serde_json::Value, styles: &Styles) -> O
let duration = format_duration_ms(prop_field(envelope, "duration_ms"));
let status_str = match prop_str_field(envelope, "status") {
Some(status) if !status.is_empty() => status,
- _ => "success",
+ _ => "succeeded",
};
let status_upper = status_str.to_uppercase();
let status_style = match status_str {
- "success" | "partial_success" => &styles.bold_green,
+ "succeeded" | "partially_succeeded" => &styles.bold_green,
_ => &styles.bold_red,
};
let cost = format_cost(
@@ -386,13 +386,17 @@ fn format_event_pretty_value(envelope: &serde_json::Value, styles: &Styles) -> O
.or_else(|| prop_field(envelope, "total_cost")),
);
- let mut lines = vec![format!(
- "{} {} {} {}",
+ let mut summary = format!(
+ "{} {} {}",
styles.dim.apply_to(&ts),
status_style.apply_to(format!("\u{2713} {status_upper}")),
styles.bold.apply_to(&duration),
- styles.dim.apply_to(&cost),
- )];
+ );
+ if !cost.is_empty() {
+ write!(summary, " {}", styles.dim.apply_to(&cost)).expect("write to string");
+ }
+
+ let mut lines = vec![summary];
if let Some(billing) =
prop_field(envelope, "billing").or_else(|| prop_field(envelope, "usage"))
@@ -968,7 +972,7 @@ mod tests {
#[test]
fn pretty_stage_completed() {
let styles = no_color_styles();
- let line = r#"{"ts":"2026-01-01T14:23:15Z","event":"stage.completed","node_label":"plan","properties":{"duration_ms":8000,"status":"success","usage":{"cost":0.12,"input_tokens":10000,"output_tokens":5200}}}"#;
+ let line = r#"{"ts":"2026-01-01T14:23:15Z","event":"stage.completed","node_label":"plan","properties":{"duration_ms":8000,"status":"succeeded","usage":{"cost":0.12,"input_tokens":10000,"output_tokens":5200}}}"#;
let result = format_event_pretty(line, &styles).unwrap();
assert!(result.contains("plan"), "got: {result}");
assert!(result.contains("$0.12"), "got: {result}");
@@ -1047,9 +1051,9 @@ mod tests {
#[test]
fn pretty_workflow_run_completed() {
let styles = no_color_styles();
- let line = r#"{"ts":"2026-01-01T14:23:32Z","run_id":"abc123","event":"run.completed","properties":{"duration_ms":25000,"status":"success","total_usd_micros":570000,"billing":{"input_tokens":5000,"output_tokens":2000,"total_tokens":7000,"cache_read_tokens":3000,"cache_write_tokens":500,"reasoning_tokens":800}}}"#;
+ let line = r#"{"ts":"2026-01-01T14:23:32Z","run_id":"abc123","event":"run.completed","properties":{"duration_ms":25000,"status":"succeeded","total_usd_micros":570000,"billing":{"input_tokens":5000,"output_tokens":2000,"total_tokens":7000,"cache_read_tokens":3000,"cache_write_tokens":500,"reasoning_tokens":800}}}"#;
let result = format_event_pretty(line, &styles).unwrap();
- assert!(result.contains("SUCCESS"), "got: {result}");
+ assert!(result.contains("SUCCEEDED"), "got: {result}");
assert!(result.contains("25s"), "got: {result}");
assert!(result.contains("$0.57"), "got: {result}");
assert!(result.contains("7.0k toks"), "got: {result}");
@@ -1063,7 +1067,7 @@ mod tests {
let styles = no_color_styles();
let line = r#"{"ts":"2026-01-01T14:23:32Z","run_id":"abc123","event":"run.completed","properties":{"duration_ms":25000,"total_cost":0.57}}"#;
let result = format_event_pretty(line, &styles).unwrap();
- assert!(result.contains("SUCCESS"), "got: {result}");
+ assert!(result.contains("SUCCEEDED"), "got: {result}");
assert!(result.contains("25s"), "got: {result}");
assert!(result.contains("$0.57"), "got: {result}");
assert!(!result.contains("Tokens:"), "got: {result}");
@@ -1072,7 +1076,7 @@ mod tests {
#[test]
fn pretty_workflow_run_completed_fail_status() {
let styles = no_color_styles();
- let line = r#"{"ts":"2026-01-01T14:23:32Z","event":"run.completed","properties":{"duration_ms":25000,"status":"fail"}}"#;
+ let line = r#"{"ts":"2026-01-01T14:23:32Z","event":"run.completed","properties":{"duration_ms":25000,"status":"failed"}}"#;
let result = format_event_pretty(line, &styles).unwrap();
assert!(result.contains("FAIL"), "got: {result}");
}
diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/event.rs b/lib/crates/fabro-cli/src/commands/run/run_progress/event.rs
index 1b6344131..23cd5d01f 100644
--- a/lib/crates/fabro-cli/src/commands/run/run_progress/event.rs
+++ b/lib/crates/fabro-cli/src/commands/run/run_progress/event.rs
@@ -509,7 +509,7 @@ mod tests {
reason: "condition".into(),
preferred_label: None,
suggested_next_ids: Vec::new(),
- stage_status: "success".into(),
+ stage_status: "succeeded".into(),
is_jump: false,
});
@@ -532,7 +532,7 @@ mod tests {
name: "Plan".into(),
index: 0,
duration_ms: 5000,
- status: "success".into(),
+ status: "succeeded".into(),
preferred_label: None,
suggested_next_ids: Vec::new(),
billing: None,
diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs b/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs
index 43e47bf21..15efe60d2 100644
--- a/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs
+++ b/lib/crates/fabro-cli/src/commands/run/run_progress/mod.rs
@@ -552,7 +552,7 @@ mod tests {
name: name.into(),
index: 0,
duration_ms: 5000,
- status: "success".into(),
+ status: "succeeded".into(),
preferred_label: None,
suggested_next_ids: Vec::new(),
billing: Some(billed_model_usage_from_llm(
@@ -616,7 +616,7 @@ mod tests {
branch: "security".into(),
index: 0,
duration_ms: 2000,
- status: "success".into(),
+ status: "succeeded".into(),
head_sha: None,
});
let stage = &ui.stage.active_stages["fork1"];
@@ -717,7 +717,7 @@ mod tests {
reason: "condition".into(),
preferred_label: None,
suggested_next_ids: Vec::new(),
- stage_status: "success".into(),
+ stage_status: "succeeded".into(),
is_jump: false,
},
Event::StageRetrying {
@@ -912,7 +912,7 @@ mod tests {
reason: "condition".into(),
preferred_label: None,
suggested_next_ids: Vec::new(),
- stage_status: "success".into(),
+ stage_status: "succeeded".into(),
is_jump: false,
});
emit(&mut ui, Event::StageRetrying {
@@ -1117,7 +1117,7 @@ mod tests {
branch: "security".into(),
index: 0,
duration_ms: 500,
- status: "success".into(),
+ status: "succeeded".into(),
head_sha: None,
});
diff --git a/lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs b/lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs
index a87861284..a7f8624bc 100644
--- a/lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs
+++ b/lib/crates/fabro-cli/src/commands/run/run_progress/stage_display.rs
@@ -261,7 +261,7 @@ impl StageDisplay {
return;
};
- let succeeded = matches!(status, "success" | "partial_success");
+ let succeeded = matches!(status, "succeeded" | "partially_succeeded");
entry.status = if succeeded {
ToolCallStatus::Succeeded
} else {
diff --git a/lib/crates/fabro-cli/src/commands/run/runner.rs b/lib/crates/fabro-cli/src/commands/run/runner.rs
index e8be3ccba..bb567a818 100644
--- a/lib/crates/fabro-cli/src/commands/run/runner.rs
+++ b/lib/crates/fabro-cli/src/commands/run/runner.rs
@@ -698,7 +698,7 @@ mod tests {
worker_title_phase_for_event(&EventBody::RunCompleted(RunCompletedProps {
duration_ms: 10,
artifact_count: 0,
- status: "success".to_string(),
+ status: "succeeded".to_string(),
reason: SuccessReason::Completed,
total_usd_micros: None,
final_git_commit_sha: None,
diff --git a/lib/crates/fabro-cli/tests/it/cmd/attach.rs b/lib/crates/fabro-cli/tests/it/cmd/attach.rs
index d0b159599..5a0ad3db2 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/attach.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/attach.rs
@@ -806,7 +806,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
"node_visits": {
"start": 1
},
- "status": "success"
+ "status": "succeeded"
},
"run_id": "[ULID]",
"stage_id": "start@1",
@@ -824,7 +824,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
"from_node": "start",
"is_jump": false,
"reason": "unconditional",
- "stage_status": "success",
+ "stage_status": "succeeded",
"to_node": "approve"
},
"run_id": "[ULID]",
@@ -860,14 +860,14 @@ fn attach_json_errors_without_prompting_for_human_input() {
"next_node_id": "approve",
"node_outcomes": {
"start": {
- "status": "success",
+ "status": "succeeded",
"usage": null
}
},
"node_visits": {
"start": 1
},
- "status": "success"
+ "status": "succeeded"
},
"run_id": "[ULID]",
"stage_id": "start@1",
diff --git a/lib/crates/fabro-cli/tests/it/cmd/inspect.rs b/lib/crates/fabro-cli/tests/it/cmd/inspect.rs
index 071e78f6a..c9dad593b 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/inspect.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/inspect.rs
@@ -191,7 +191,7 @@ fn inspect_completed_run_shows_run_start_conclusion_checkpoint() {
"has_start_time": true
},
"conclusion": {
- "status": "success",
+ "status": "succeeded",
"duration_ms": "[DURATION_MS]",
"stage_count": null
},
@@ -263,7 +263,7 @@ fn inspect_completed_run_reads_store_without_disk_metadata_files() {
"has_start_time": true
},
"conclusion": {
- "status": "success",
+ "status": "succeeded",
"duration_ms": "[DURATION_MS]",
"stage_count": null
},
@@ -322,7 +322,7 @@ fn inspect_git_backed_run_exposes_checkpoint_and_sandbox_state() {
"base_sha": "[SHA]"
},
"conclusion": {
- "status": "success",
+ "status": "succeeded",
"duration_ms": "[DURATION_MS]",
"final_git_commit_sha": "[SHA]",
"stage_count": null
diff --git a/lib/crates/fabro-cli/tests/it/cmd/logs.rs b/lib/crates/fabro-cli/tests/it/cmd/logs.rs
index 6b08a28a4..1b0268a0b 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/logs.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/logs.rs
@@ -203,7 +203,7 @@ fn logs_pretty_formats_small_run() {
[CLOCK] → exit unconditional
[CLOCK] ▶ Exit
[CLOCK] ✓ Exit [DURATION]
- [CLOCK] ✓ SUCCESS [DURATION]
+ [CLOCK] ✓ SUCCEEDED [DURATION]
----- stderr -----
");
}
diff --git a/lib/crates/fabro-cli/tests/it/cmd/pr_create.rs b/lib/crates/fabro-cli/tests/it/cmd/pr_create.rs
index 55984b851..e5773b31d 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/pr_create.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/pr_create.rs
@@ -30,7 +30,7 @@ fn help() {
--server Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
--model LLM model for generating PR description
- -f, --force Create PR even if the run status is not success/partial_success
+ -f, --force Create PR even if the run status is not succeeded/partially_succeeded
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
--quiet Suppress non-essential output [env: FABRO_QUIET=]
--verbose Enable verbose output [env: FABRO_VERBOSE=]
diff --git a/lib/crates/fabro-cli/tests/it/cmd/run.rs b/lib/crates/fabro-cli/tests/it/cmd/run.rs
index 58ce83937..2dc49058d 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/run.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/run.rs
@@ -51,7 +51,7 @@ fn remote_run_state_response() -> serde_json::Value {
"checkpoints": [],
"conclusion": {
"timestamp": "2026-04-05T12:00:01Z",
- "status": "success",
+ "status": "succeeded",
"duration_ms": 12,
"stages": [],
"billing": null,
@@ -77,7 +77,7 @@ fn run_completed_event(run_id: &str) -> serde_json::Value {
"properties": {
"duration_ms": 12,
"artifact_count": 0,
- "status": "success",
+ "status": "succeeded",
"reason": "completed"
}
})
@@ -703,7 +703,7 @@ fn dry_run_simple() {
=== Run Result ===
Run: [ULID]
- Status: SUCCESS
+ Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
diff --git a/lib/crates/fabro-cli/tests/it/cmd/runner.rs b/lib/crates/fabro-cli/tests/it/cmd/runner.rs
index 3f9cfb707..4783ff2ca 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/runner.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/runner.rs
@@ -58,7 +58,7 @@ fn assert_worker_succeeded(run_dir: &std::path::Path, stdout: &[u8]) {
let events = stored_worker_events(run_dir);
assert!(events.iter().any(|event| matches!(
&event.body,
- EventBody::RunCompleted(props) if props.status == "success"
+ EventBody::RunCompleted(props) if props.status == "succeeded"
)));
}
@@ -656,7 +656,7 @@ digraph Test {
"run_dir": null,
"start_time": "[TIMESTAMP]",
"conclusion_timestamp": "[TIMESTAMP]",
- "conclusion_status": "success"
+ "conclusion_status": "succeeded"
}
"#);
diff --git a/lib/crates/fabro-cli/tests/it/cmd/support.rs b/lib/crates/fabro-cli/tests/it/cmd/support.rs
index a6f37c683..b75631945 100644
--- a/lib/crates/fabro-cli/tests/it/cmd/support.rs
+++ b/lib/crates/fabro-cli/tests/it/cmd/support.rs
@@ -1042,7 +1042,7 @@ async fn append_seeded_simple_completion_events(
serde_json::json!({
"duration_ms": 123,
"artifact_count": 0,
- "status": "success",
+ "status": "succeeded",
"reason": "completed",
"total_usd_micros": null,
"final_git_commit_sha": null,
@@ -1157,7 +1157,14 @@ async fn append_seeded_git_completion_events(
&run.run_id,
Some("start"),
"checkpoint.completed",
- checkpoint_properties("success", "start", &["start"], Some("step_one"), None, None),
+ checkpoint_properties(
+ "succeeded",
+ "start",
+ &["start"],
+ Some("step_one"),
+ None,
+ None,
+ ),
)
.await;
append_run_event(
@@ -1201,7 +1208,7 @@ async fn append_seeded_git_completion_events(
serde_json::json!({
"duration_ms": 456,
"artifact_count": 0,
- "status": "success",
+ "status": "succeeded",
"reason": "completed",
"total_usd_micros": null,
"final_git_commit_sha": step_two_sha,
@@ -1262,7 +1269,7 @@ async fn append_seeded_git_noop_events(
serde_json::json!({
"duration_ms": 123,
"artifact_count": 0,
- "status": "success",
+ "status": "succeeded",
"reason": "completed",
"total_usd_micros": null,
"final_git_commit_sha": base_sha,
@@ -1322,7 +1329,7 @@ async fn append_seeded_artifact_run_events(
serde_json::json!({
"duration_ms": 123,
"artifact_count": 6,
- "status": "success",
+ "status": "succeeded",
"reason": "completed",
"total_usd_micros": null,
"final_git_commit_sha": null,
@@ -1415,7 +1422,7 @@ async fn append_seeded_edge(
"reason": "unconditional",
"preferred_label": null,
"suggested_next_ids": [],
- "stage_status": "success",
+ "stage_status": "succeeded",
"is_jump": false,
}),
)
@@ -1470,7 +1477,7 @@ fn stage_completed_properties(index: usize, response: Option<&str>) -> serde_jso
serde_json::json!({
"index": index,
"duration_ms": 1,
- "status": "success",
+ "status": "succeeded",
"preferred_label": null,
"suggested_next_ids": [],
"billing": null,
diff --git a/lib/crates/fabro-cli/tests/it/scenario/smoke.rs b/lib/crates/fabro-cli/tests/it/scenario/smoke.rs
index 8d6616915..ddd607c85 100644
--- a/lib/crates/fabro-cli/tests/it/scenario/smoke.rs
+++ b/lib/crates/fabro-cli/tests/it/scenario/smoke.rs
@@ -35,7 +35,7 @@ fn run_sse_body(run_id: &str) -> String {
"properties": {
"duration_ms": 12,
"artifact_count": 0,
- "status": "success",
+ "status": "succeeded",
"reason": "completed"
}
});
diff --git a/lib/crates/fabro-cli/tests/it/workflow/agent_linear.rs b/lib/crates/fabro-cli/tests/it/workflow/agent_linear.rs
index d423c5eb4..c0eaffa53 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/agent_linear.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/agent_linear.rs
@@ -26,7 +26,7 @@ fn scenario_agent_linear(sandbox: &str) {
let run_dir = find_run_dir(&context);
let conclusion = read_conclusion(&run_dir);
- assert_eq!(conclusion["status"].as_str(), Some("success"));
+ assert_eq!(conclusion["status"].as_str(), Some("succeeded"));
let nodes = completed_nodes(&run_dir);
assert!(
diff --git a/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs b/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs
index d0fbabe4f..d01318b69 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/command_agent_mixed.rs
@@ -32,7 +32,7 @@ fn scenario_command_agent_mixed(sandbox: &str) {
let run_dir = find_run_dir(&context);
let conclusion = read_conclusion(&run_dir);
- assert_eq!(conclusion["status"].as_str(), Some("success"));
+ assert_eq!(conclusion["status"].as_str(), Some("succeeded"));
let nodes = completed_nodes(&run_dir);
assert!(
diff --git a/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs b/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs
index 7c1d7eae7..9120c123b 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/command_pipeline.rs
@@ -33,8 +33,8 @@ fn scenario_command_pipeline(sandbox: &str) {
let conclusion = read_conclusion(&run_dir);
assert_eq!(
conclusion["status"].as_str(),
- Some("success"),
- "conclusion status should be success"
+ Some("succeeded"),
+ "conclusion status should be succeeded"
);
let nodes = completed_nodes(&run_dir);
diff --git a/lib/crates/fabro-cli/tests/it/workflow/conditional_branching.rs b/lib/crates/fabro-cli/tests/it/workflow/conditional_branching.rs
index db433f37e..71e273420 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/conditional_branching.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/conditional_branching.rs
@@ -17,7 +17,7 @@ fn scenario_conditional_branching(sandbox: &str) {
let run_dir = find_run_dir(&context);
let conclusion = read_conclusion(&run_dir);
- assert_eq!(conclusion["status"].as_str(), Some("success"));
+ assert_eq!(conclusion["status"].as_str(), Some("succeeded"));
let nodes = completed_nodes(&run_dir);
assert!(
diff --git a/lib/crates/fabro-cli/tests/it/workflow/dry_run_examples.rs b/lib/crates/fabro-cli/tests/it/workflow/dry_run_examples.rs
index a44ef957d..04ccedce5 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/dry_run_examples.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/dry_run_examples.rs
@@ -30,7 +30,7 @@ fn dry_run_branching() {
=== Run Result ===
Run: [ULID]
- Status: SUCCESS
+ Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
@@ -63,7 +63,7 @@ fn dry_run_conditions() {
=== Run Result ===
Run: [ULID]
- Status: SUCCESS
+ Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
@@ -97,7 +97,7 @@ fn dry_run_parallel() {
=== Run Result ===
Run: [ULID]
- Status: SUCCESS
+ Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
@@ -131,7 +131,7 @@ fn dry_run_styled() {
=== Run Result ===
Run: [ULID]
- Status: SUCCESS
+ Status: SUCCEEDED
Duration: [DURATION]
=== Output ===
@@ -163,7 +163,7 @@ fn dry_run_legacy_tool() {
=== Run Result ===
Run: [ULID]
- Status: SUCCESS
+ Status: SUCCEEDED
Duration: [DURATION]
");
}
diff --git a/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs b/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs
index a2a1feff4..555c53fcf 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/full_stack.rs
@@ -34,7 +34,7 @@ fn scenario_full_stack(sandbox: &str) {
let conclusion = read_conclusion(&run_dir);
assert_eq!(
conclusion["status"].as_str(),
- Some("success"),
+ Some("succeeded"),
"conclusion: {conclusion}"
);
assert!(
diff --git a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs b/lib/crates/fabro-cli/tests/it/workflow/hooks.rs
index 524910f1a..df15350e9 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/hooks.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/hooks.rs
@@ -124,7 +124,7 @@ model = "{model}"
run_success_output(cmd).await;
}
- assert_eq!(conclusion_status(&context), "success");
+ assert_eq!(conclusion_status(&context), "succeeded");
}
#[fabro_macros::e2e_test(twin, live("ANTHROPIC_API_KEY"))]
@@ -230,7 +230,7 @@ max_tool_rounds = 1
run_success_output(cmd).await;
}
- assert_eq!(conclusion_status(&context), "success");
+ assert_eq!(conclusion_status(&context), "succeeded");
}
#[fabro_macros::e2e_test(twin, live("ANTHROPIC_API_KEY"))]
@@ -287,7 +287,7 @@ max_tool_rounds = 5
run_success_output(cmd).await;
}
- assert_eq!(conclusion_status(&context), "success");
+ assert_eq!(conclusion_status(&context), "succeeded");
}
#[fabro_macros::e2e_test(twin, live("ANTHROPIC_API_KEY"))]
@@ -346,5 +346,5 @@ async fn arc_e2e_with_real_llm() {
"Hello from LLM",
"workflow should create the expected file"
);
- assert_eq!(conclusion_status(&context), "success");
+ assert_eq!(conclusion_status(&context), "succeeded");
}
diff --git a/lib/crates/fabro-cli/tests/it/workflow/human_gate.rs b/lib/crates/fabro-cli/tests/it/workflow/human_gate.rs
index 3a530f31e..8c11f651e 100644
--- a/lib/crates/fabro-cli/tests/it/workflow/human_gate.rs
+++ b/lib/crates/fabro-cli/tests/it/workflow/human_gate.rs
@@ -24,7 +24,7 @@ fn scenario_human_gate(sandbox: &str) {
let run_dir = find_run_dir(&context);
let conclusion = read_conclusion(&run_dir);
- assert_eq!(conclusion["status"].as_str(), Some("success"));
+ assert_eq!(conclusion["status"].as_str(), Some("succeeded"));
let nodes = completed_nodes(&run_dir);
assert!(
diff --git a/lib/crates/fabro-core/src/executor.rs b/lib/crates/fabro-core/src/executor.rs
index a9973972a..d92a89daf 100644
--- a/lib/crates/fabro-core/src/executor.rs
+++ b/lib/crates/fabro-core/src/executor.rs
@@ -443,7 +443,7 @@ mod tests {
use crate::context::Context;
use crate::error::HandlerErrorDetail;
use crate::lifecycle::RunLifecycle;
- use crate::outcome::{StageOutcome, StageStatus};
+ use crate::outcome::StageOutcome;
use crate::retry::{BackoffPolicy, RetryPolicy};
use crate::test_fixtures::*;
@@ -470,7 +470,7 @@ mod tests {
let result = run_linear(&["start", "work", "end"], Arc::new(AlwaysSucceedHandler))
.await
.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -514,7 +514,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -524,7 +524,7 @@ mod tests {
ExecutorBuilder::new(Arc::new(AlwaysSucceedHandler) as Arc>)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -535,7 +535,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -550,7 +550,7 @@ mod tests {
let executor =
ExecutorBuilder::new(handler.clone() as Arc>).build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
assert_eq!(handler.calls(), 2);
}
@@ -559,7 +559,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -571,7 +571,9 @@ mod tests {
)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Fail);
+ assert_eq!(result.status, StageOutcome::Failed {
+ retry_requested: false,
+ });
}
#[tokio::test]
@@ -726,7 +728,7 @@ mod tests {
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
// Ends at "bad" terminal with success (goal gates pass since no gates defined)
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -748,7 +750,7 @@ mod tests {
ExecutorBuilder::new(Arc::new(AlwaysSucceedHandler) as Arc>)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -781,7 +783,7 @@ mod tests {
let executor =
ExecutorBuilder::new(Arc::new(JumpHandler) as Arc>).build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -817,7 +819,7 @@ mod tests {
.max_node_visits(5)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
assert_eq!(handler.calls(), 4);
}
@@ -881,7 +883,9 @@ mod tests {
)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Fail);
+ assert_eq!(result.status, StageOutcome::Failed {
+ retry_requested: false,
+ });
}
#[tokio::test]
@@ -893,7 +897,7 @@ mod tests {
ExecutorBuilder::new(Arc::new(AlwaysSucceedHandler) as Arc>)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
// ---- Step 11: Cancellation ----
@@ -1003,7 +1007,7 @@ mod tests {
)
.await
.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
assert_eq!(handler.calls(), 3);
}
@@ -1041,7 +1045,7 @@ mod tests {
)
.await
.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
assert_eq!(handler.calls(), 3);
}
@@ -1119,20 +1123,20 @@ mod tests {
}
fn on_retries_exhausted(&self, _n: &TestNode, _last: Outcome) -> Outcome {
Outcome {
- status: StageStatus::PartialSuccess,
+ status: StageOutcome::PartiallySucceeded,
notes: Some("exhausted".into()),
..Outcome::default()
}
}
}
- // No outgoing edges from "start" so PartialSuccess becomes the run result
+ // No outgoing edges from "start" so PartiallySucceeded becomes the run result.
let g = TestGraph::new(vec![TestNode::new("start")], vec![], "start");
let state = ExecutionState::new(&g).unwrap();
let executor =
ExecutorBuilder::new(Arc::new(ExhaustedHandler) as Arc>)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::PartialSuccess);
+ assert_eq!(result.status, StageOutcome::PartiallySucceeded);
}
#[tokio::test]
@@ -1303,7 +1307,7 @@ mod tests {
.lifecycle(Box::new(SkipOnSecondAttempt(call_count_clone)))
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success); // overall run succeeds via terminal
+ assert_eq!(result.status, StageOutcome::Succeeded); // overall run succeeds via terminal
assert_eq!(handler.calls(), 1); // handler only called once
assert_eq!(call_count.load(Ordering::Relaxed), 2); // before_attempt called twice
}
@@ -1338,7 +1342,7 @@ mod tests {
)
.await
.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
// Should have slept ~5s for the retry backoff
assert!(start.elapsed() >= Duration::from_secs(4));
}
@@ -1372,7 +1376,7 @@ mod tests {
.lifecycle(Box::new(SkipFirst(Mutex::new(false))))
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -1451,7 +1455,7 @@ mod tests {
.lifecycle(Box::new(Redirector))
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -1777,7 +1781,7 @@ mod tests {
let g2 = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -1926,7 +1930,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -1937,7 +1941,7 @@ mod tests {
let executor =
ExecutorBuilder::new(handler.clone() as Arc>).build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
assert_eq!(handler.calls(), 2);
}
@@ -1968,7 +1972,7 @@ mod tests {
.max_node_visits(5)
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
assert_eq!(handler.calls(), 2);
}
@@ -1993,7 +1997,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -2008,7 +2012,9 @@ mod tests {
.build();
let (result, _) = executor.run(&g, state).await.unwrap();
- assert_eq!(result.status, StageStatus::Fail);
+ assert_eq!(result.status, StageOutcome::Failed {
+ retry_requested: false,
+ });
assert_eq!(
result
.failure
diff --git a/lib/crates/fabro-core/src/lib.rs b/lib/crates/fabro-core/src/lib.rs
index a9c2f28c1..853d4eeaf 100644
--- a/lib/crates/fabro-core/src/lib.rs
+++ b/lib/crates/fabro-core/src/lib.rs
@@ -23,7 +23,7 @@ pub use lifecycle::{
};
pub use outcome::{
FailureCategory, FailureDetail, NodeResult, NodeResultExt, Outcome, OutcomeMeta, StageOutcome,
- StageState, StageStatus,
+ StageState,
};
pub use retry::{BackoffPolicy, RetryPolicy};
pub use stall::{ActivityMonitor, StallGuard, StallWatchdog};
diff --git a/lib/crates/fabro-core/src/outcome.rs b/lib/crates/fabro-core/src/outcome.rs
index 34300f466..53d590fbf 100644
--- a/lib/crates/fabro-core/src/outcome.rs
+++ b/lib/crates/fabro-core/src/outcome.rs
@@ -2,7 +2,6 @@ use std::time::Duration;
pub use fabro_types::outcome::{
FailureCategory, FailureDetail, NodeResult, Outcome, OutcomeMeta, StageOutcome, StageState,
- StageStatus,
};
use crate::error::Error;
diff --git a/lib/crates/fabro-core/src/state.rs b/lib/crates/fabro-core/src/state.rs
index 24dd1a0dd..af37e89dd 100644
--- a/lib/crates/fabro-core/src/state.rs
+++ b/lib/crates/fabro-core/src/state.rs
@@ -93,7 +93,7 @@ mod tests {
use serde_json::json;
use super::*;
- use crate::outcome::{Outcome, StageStatus};
+ use crate::outcome::{Outcome, StageOutcome};
use crate::test_fixtures::linear_graph;
#[test]
@@ -115,7 +115,7 @@ mod tests {
state.record("start", &result);
assert_eq!(state.completed_nodes, vec!["start"]);
- assert_eq!(state.node_outcomes["start"].status, StageStatus::Success);
+ assert_eq!(state.node_outcomes["start"].status, StageOutcome::Succeeded);
assert_eq!(state.node_retries["start"], 1); // 2 attempts - 1
assert_eq!(state.stage_index, 1);
}
diff --git a/lib/crates/fabro-core/src/test_fixtures.rs b/lib/crates/fabro-core/src/test_fixtures.rs
index 911a37b51..d87381059 100644
--- a/lib/crates/fabro-core/src/test_fixtures.rs
+++ b/lib/crates/fabro-core/src/test_fixtures.rs
@@ -8,7 +8,7 @@ use crate::context::Context;
use crate::error::{Error, HandlerErrorDetail, Result};
use crate::graph::{EdgeSelection, EdgeSpec, Graph, NodeSpec};
use crate::handler::NodeHandler;
-use crate::outcome::{Outcome, StageStatus};
+use crate::outcome::{Outcome, StageOutcome};
use crate::retry::RetryPolicy;
// ---- Test node ----
@@ -18,7 +18,7 @@ pub struct TestNode {
pub id: String,
pub terminal: bool,
pub max_visits: Option,
- pub goal_gate: Option<(String, StageStatus)>,
+ pub goal_gate: Option<(String, StageOutcome)>,
}
impl TestNode {
@@ -47,7 +47,7 @@ impl TestNode {
}
#[must_use]
- pub fn with_goal_gate(mut self, node_id: &str, required_status: StageStatus) -> Self {
+ pub fn with_goal_gate(mut self, node_id: &str, required_status: StageOutcome) -> Self {
self.goal_gate = Some((node_id.to_string(), required_status));
self
}
@@ -525,7 +525,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -540,7 +540,7 @@ mod tests {
let g = TestGraph::new(
vec![
TestNode::new("work"),
- TestNode::terminal("end").with_goal_gate("work", StageStatus::Success),
+ TestNode::terminal("end").with_goal_gate("work", StageOutcome::Succeeded),
],
vec![TestEdge::new("work", "end")],
"work",
@@ -564,7 +564,7 @@ mod tests {
let node = g.get_node("start").unwrap();
let ctx = Context::new();
let result = h.execute(&node, &ctx, &g).await.unwrap();
- assert_eq!(result.status, StageStatus::Success);
+ assert_eq!(result.status, StageOutcome::Succeeded);
}
#[tokio::test]
@@ -574,7 +574,9 @@ mod tests {
let node = g.get_node("start").unwrap();
let ctx = Context::new();
let result = h.execute(&node, &ctx, &g).await.unwrap();
- assert_eq!(result.status, StageStatus::Fail);
+ assert_eq!(result.status, StageOutcome::Failed {
+ retry_requested: false,
+ });
assert_eq!(result.failure.unwrap().message, "boom");
}
@@ -586,16 +588,18 @@ mod tests {
let ctx = Context::new();
let r1 = h.execute(&node, &ctx, &g).await.unwrap();
- assert_eq!(r1.status, StageStatus::Fail);
+ assert_eq!(r1.status, StageOutcome::Failed {
+ retry_requested: false,
+ });
assert_eq!(h.calls(), 1);
let r2 = h.execute(&node, &ctx, &g).await.unwrap();
- assert_eq!(r2.status, StageStatus::Success);
+ assert_eq!(r2.status, StageOutcome::Succeeded);
assert_eq!(h.calls(), 2);
// Past end of outcomes list → default success
let r3 = h.execute(&node, &ctx, &g).await.unwrap();
- assert_eq!(r3.status, StageStatus::Success);
+ assert_eq!(r3.status, StageOutcome::Succeeded);
assert_eq!(h.calls(), 3);
}
}
diff --git a/lib/crates/fabro-retro/src/retro_agent.rs b/lib/crates/fabro-retro/src/retro_agent.rs
index 4792a2f75..6a66ada28 100644
--- a/lib/crates/fabro-retro/src/retro_agent.rs
+++ b/lib/crates/fabro-retro/src/retro_agent.rs
@@ -479,7 +479,7 @@ mod tests {
use chrono::{TimeZone, Utc};
use fabro_agent::LocalSandbox;
use fabro_store::{NodeState, StageId};
- use fabro_types::{NodeStatusRecord, StageStatus};
+ use fabro_types::{NodeStatusRecord, StageOutcome};
use tokio::fs;
use super::*;
@@ -558,7 +558,7 @@ mod tests {
prompt: Some("plan".to_string()),
response: Some("done".to_string()),
status: Some(NodeStatusRecord {
- status: StageStatus::Success,
+ status: StageOutcome::Succeeded,
notes: Some("ok".to_string()),
failure_reason: None,
timestamp: Utc
diff --git a/lib/crates/fabro-store/src/run_state.rs b/lib/crates/fabro-store/src/run_state.rs
index 77100a86e..862291daa 100644
--- a/lib/crates/fabro-store/src/run_state.rs
+++ b/lib/crates/fabro-store/src/run_state.rs
@@ -9,7 +9,7 @@ use fabro_types::run_event::{
use fabro_types::{
BilledModelUsage, Checkpoint, Conclusion, EventBody, FailureSignature, InterviewQuestionRecord,
NodeStatusRecord, Outcome, PendingInterviewRecord, PullRequestRecord, RunControlAction, RunId,
- RunProjection, RunSpec, RunStatus, RunSummary, SandboxRecord, StageStatus, StartRecord,
+ RunProjection, RunSpec, RunStatus, RunSummary, SandboxRecord, StageOutcome, StartRecord,
TerminalStatus,
};
use fabro_util::error::render_with_causes;
@@ -302,7 +302,9 @@ impl RunProjectionReducer for RunProjection {
let failure_reason = props.failure.as_ref().map(|detail| detail.message.clone());
let node = self.node_mut(node_id, visit);
node.status = Some(NodeStatusRecord {
- status: StageStatus::Fail,
+ status: StageOutcome::Failed {
+ retry_requested: false,
+ },
notes: None,
failure_reason,
timestamp: ts,
@@ -448,7 +450,7 @@ fn conclusion_from_completed(
) -> Result {
Ok(Conclusion {
timestamp,
- status: StageStatus::from_str(&props.status)
+ status: StageOutcome::from_str(&props.status)
.map_err(|err| Error::InvalidEvent(format!("invalid completed stage status: {err}")))?,
duration_ms: props.duration_ms,
failure_reason: None,
@@ -462,7 +464,9 @@ fn conclusion_from_completed(
fn conclusion_from_failed(props: &RunFailedProps, timestamp: DateTime) -> Conclusion {
Conclusion {
timestamp,
- status: StageStatus::Fail,
+ status: StageOutcome::Failed {
+ retry_requested: false,
+ },
duration_ms: props.duration_ms,
failure_reason: Some(render_with_causes(&props.error, &props.causes)),
final_git_commit_sha: props.git_commit_sha.clone(),
@@ -485,7 +489,7 @@ fn stage_visit(
fn stage_outcome_from_props(props: &StageCompletedProps) -> Outcome