refactor(types): remove stage status compatibility

This commit is contained in:
Bryan Helmkamp 2026-04-30 06:48:47 -04:00
parent ba529c23f9
commit d65c1fa635
No known key found for this signature in database
72 changed files with 552 additions and 549 deletions

View file

@ -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;

View file

@ -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. |

View file

@ -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
}
}

View file

@ -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"]
}
```

View file

@ -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"]

View file

@ -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.

View file

@ -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 |

View file

@ -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"]

View file

@ -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) |
<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`.
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.
</Note>
## 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.

View file

@ -591,7 +591,7 @@ fabro pr create [OPTIONS] <RUN_ID>
| 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 <model>` | LLM model for generating PR description |
| `--server <server>` | Fabro server target: http(s) URL or absolute Unix socket path |

View file

@ -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) |

View file

@ -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) |

View file

@ -767,7 +767,7 @@ pub(crate) struct PrCreateArgs {
/// LLM model for generating PR description
#[arg(long)]
pub(crate) model: Option<String>,
/// 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,
}

View file

@ -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<ExitCode> {
fn event_exit_code(event: &EventEnvelope) -> Option<ExitCode> {
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)

View file

@ -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}");
}

View file

@ -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,

View file

@ -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,
});

View file

@ -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 {

View file

@ -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,

View file

@ -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",

View file

@ -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

View file

@ -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 -----
");
}

View file

@ -30,7 +30,7 @@ fn help() {
--server <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 <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=]

View file

@ -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 ===

View file

@ -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"
}
"#);

View file

@ -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,

View file

@ -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"
}
});

View file

@ -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!(

View file

@ -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!(

View file

@ -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);

View file

@ -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!(

View file

@ -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]
");
}

View file

@ -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!(

View file

@ -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");
}

View file

@ -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!(

View file

@ -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<dyn NodeHandler<TestGraph>>)
.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<dyn NodeHandler<TestGraph>>).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<dyn NodeHandler<TestGraph>>)
.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<dyn NodeHandler<TestGraph>>).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<dyn NodeHandler<TestGraph>>)
.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<dyn NodeHandler<TestGraph>>)
.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<dyn NodeHandler<TestGraph>>).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

View file

@ -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};

View file

@ -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;

View file

@ -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);
}

View file

@ -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<usize>,
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);
}
}

View file

@ -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

View file

@ -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<Conclusion> {
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<Utc>) -> 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<Option<BilledModelUsage>> {
Outcome {
status: props.status.clone(),
status: props.status,
preferred_label: props.preferred_label.clone(),
suggested_next_ids: props.suggested_next_ids.clone(),
context_updates: props
@ -508,7 +512,7 @@ fn node_status_from_outcome(
timestamp: DateTime<Utc>,
) -> NodeStatusRecord {
NodeStatusRecord {
status: outcome.status.clone(),
status: outcome.status,
notes: outcome.notes.clone(),
failure_reason: outcome
.failure
@ -1118,7 +1122,7 @@ mod tests {
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,
@ -1181,7 +1185,7 @@ mod tests {
EventBody::RunCompleted(RunCompletedProps {
duration_ms: 10,
artifact_count: 0,
status: "success".to_string(),
status: "succeeded".to_string(),
reason: SuccessReason::PartialSuccess,
total_usd_micros: None,
final_git_commit_sha: None,
@ -1311,7 +1315,7 @@ mod tests {
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,

View file

@ -421,7 +421,7 @@ mod tests {
&serde_json::json!({
"duration_ms": 3210,
"artifact_count": 1,
"status": "success",
"status": "succeeded",
"reason": "completed",
"total_cost": 1.25,
}),
@ -625,7 +625,7 @@ mod tests {
&serde_json::json!({
"duration_ms": 3210,
"artifact_count": 1,
"status": "success",
"status": "succeeded",
"reason": "completed",
"total_cost": 1.25,
}),

View file

@ -5,7 +5,7 @@ use fabro_store::{NodeState, RunProjection, SerializableProjection, StageId};
use fabro_types::graph::Graph;
use fabro_types::run::RunSpec;
use fabro_types::{
Checkpoint, NodeStatusRecord, RunStatus, SandboxRecord, StageStatus, StartRecord,
Checkpoint, NodeStatusRecord, RunStatus, SandboxRecord, StageOutcome, StartRecord,
TerminalStatus, WorkflowSettings, fixtures,
};
use serde_json::json;
@ -81,7 +81,7 @@ fn serializable_projection_round_trips_and_trims_bulky_node_fields() {
prompt: Some("plan the work".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

View file

@ -52,7 +52,6 @@ pub use interview::{InterviewQuestionRecord, QuestionType};
pub use node_status::NodeStatusRecord;
pub use outcome::{
FailureCategory, FailureDetail, NodeResult, Outcome, OutcomeMeta, StageOutcome, StageState,
StageStatus,
};
pub use pull_request::{
PullRequestDetail, PullRequestGithubDetail, PullRequestRecord, PullRequestRef, PullRequestUser,

View file

@ -3,7 +3,7 @@ use std::fmt;
use std::str::FromStr;
use std::time::Duration;
use serde::de::DeserializeOwned;
use serde::de::{DeserializeOwned, Error as DeError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use strum::{Display, EnumString, IntoStaticStr};
@ -87,7 +87,7 @@ impl<'de> Deserialize<'de> for StageOutcome {
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
value.parse().map_err(serde::de::Error::custom)
value.parse().map_err(DeError::custom)
}
}
@ -142,23 +142,6 @@ impl From<StageOutcome> for StageState {
}
}
#[allow(
non_upper_case_globals,
reason = "Temporary compatibility constants allow the staged refactor to compile between phase commits."
)]
impl StageOutcome {
pub const Success: Self = Self::Succeeded;
pub const Fail: Self = Self::Failed {
retry_requested: false,
};
pub const PartialSuccess: Self = Self::PartiallySucceeded;
pub const Retry: Self = Self::Failed {
retry_requested: true,
};
}
pub type StageStatus = StageOutcome;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FailureCategory {

View file

@ -8,7 +8,7 @@ A DOT-based pipeline runner for multi-stage AI workflows. Define workflows as Gr
- **Node** -- A workflow step. Graphviz shapes map to handler types (e.g., `Mdiamond` = start, `Msquare` = exit, `box` = agent, `tab` = prompt, `diamond` = conditional, `hexagon` = human gate, `component` = parallel).
- **Edge** -- A connection between nodes with optional `condition`, `label`, `weight`, and `fidelity` attributes that control routing.
- **Handler** -- An async trait implementation that executes a node and returns an `Outcome`. Built-in handlers include `StartHandler`, `ExitHandler`, `AgentHandler`, `PromptHandler`, `ConditionalHandler`, `HumanHandler`, `ParallelHandler`, `FanInHandler`, `CommandHandler`, and `SubWorkflowHandler`.
- **Outcome** -- The result of executing a handler, carrying a `StageStatus` (Success, Fail, PartialSuccess, Retry, Skipped), optional routing hints (`preferred_label`, `suggested_next_ids`), and context updates.
- **Outcome** -- The result of executing a handler, carrying a `StageOutcome` (Success, Fail, PartialSuccess, Retry, Skipped), optional routing hints (`preferred_label`, `suggested_next_ids`), and context updates.
- **Context** -- A thread-safe key-value store shared across pipeline stages, supporting snapshots and isolated cloning for parallel branches.
- **Interviewer** -- A trait for human-in-the-loop interactions. Implementations include `AutoApproveInterviewer`, `QueueInterviewer`, `CallbackInterviewer`, `ConsoleInterviewer`, and `RecordingInterviewer`.
- **Checkpoint** -- A serializable snapshot of execution state (completed nodes, context values) for crash recovery and resume.

View file

@ -302,7 +302,7 @@ impl Handler for SubWorkflowHandler {
);
let mut outcome = Outcome {
status: child_outcome.status.clone(),
status: child_outcome.status,
notes: Some(format!("Child completed at cycle {cycle}")),
context_updates: diff,
..Outcome::success()

View file

@ -24,7 +24,7 @@ pub fn classify_engine_result(
) -> (StageOutcome, Option<String>, RunStatus) {
match engine_result {
Ok(outcome) => {
let status = outcome.status.clone();
let status = outcome.status;
let failure_reason = outcome.failure_reason().map(String::from);
let run_status = match status {
StageOutcome::Succeeded | StageOutcome::Skipped => RunStatus::Succeeded {
@ -359,7 +359,7 @@ pub(crate) fn build_terminal_event(
StageOutcome::Failed {
retry_requested: false,
},
|o| o.status.clone(),
|o| o.status,
);
if outcome_status == StageOutcome::Succeeded
@ -456,7 +456,7 @@ pub async fn finalize(retroed: Retroed, options: &FinalizeOptions) -> Result<Con
let conclusion = build_conclusion_from_parts(
checkpoint.as_ref(),
&stage_durations,
final_status.clone(),
final_status,
failure_reason,
duration_ms,
options.last_git_sha.clone(),

View file

@ -19,7 +19,7 @@
*/
export interface CreateRunPullRequestRequest {
/**
* Create the pull request even if the run did not finish with success or partial_success.
* Create the pull request even if the run did not finish with succeeded or partially_succeeded.
*/
'force': boolean;
/**
@ -27,4 +27,3 @@ export interface CreateRunPullRequestRequest {
*/
'model'?: string | null;
}

View file

@ -111,7 +111,7 @@ digraph Workflow {
node_type="stack.steer", label="Review Consensus", shape="box", style="rounded,filled",
is_codergen="true", model="gpt-5.2-codex",
max_agent_turns="6", timeout="300",
llm_prompt="Reach consensus.\n\nRead .ai/review_*.md\nWrite to .ai/review_consensus.md\n\noutcome=yes if PASS, outcome=retry if FAIL. Set preferred_next_label to \"\" (empty) or to \"yes\"/\"retry\"; do not use \"No\"."
llm_prompt="Reach consensus.\n\nRead .ai/review_*.md\nWrite to .ai/review_consensus.md\n\noutcome=succeeded if PASS, outcome=failed if FAIL. Set preferred_next_label to \"\" (empty) or to \"yes\"/\"retry\"; do not use \"No\"."
];
Postmortem [
@ -150,7 +150,7 @@ digraph Workflow {
ReviewGemini -> ReviewConsensus;
ReviewGPT -> ReviewConsensus;
ReviewOpus -> ReviewConsensus;
ReviewConsensus -> Exit [condition="outcome=yes"];
ReviewConsensus -> Exit [condition="outcome=succeeded"];
ReviewConsensus -> Postmortem;
Postmortem -> PlanGemini [loop_restart="true"];
Postmortem -> PlanGPT [loop_restart="true"];

View file

@ -30,7 +30,7 @@ Create:
Run: go build ./...
Write status.json: outcome=success if project builds, outcome=fail with error otherwise."
Write status.json: outcome=succeeded if project builds, outcome=failed with error otherwise."
]
verify_setup [
@ -45,7 +45,7 @@ Run:
3. Check that go.mod exists with correct module path
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -69,7 +69,7 @@ Add comprehensive godoc comments. Include validation methods where appropriate.
Run: go build ./... && go test ./pkg/dttf/...
Write status.json: outcome=success if builds and tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if builds and tests pass, outcome=failed otherwise."
]
verify_types [
@ -85,7 +85,7 @@ Run:
4. Check that all types from section 7 are present
Write results to .ai/verify_types.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_types [shape=diamond, label="Types OK?"]
@ -112,7 +112,7 @@ Create tests in pkg/dttf/loader_test.go with sample PNGs.
Run: go test ./pkg/dttf/... -run TestLoad
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_loader [
@ -128,7 +128,7 @@ Run:
4. Verify font.json defaults are applied
Write results to .ai/verify_loader.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_loader [shape=diamond, label="Loader OK?"]
@ -151,7 +151,7 @@ Create tests with synthetic images and known thresholds.
Run: go test ./pkg/dttf/... -run TestImage
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_imageproc [
@ -166,7 +166,7 @@ Run:
3. Check threshold produces binary output
Write results to .ai/verify_imageproc.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_imageproc [shape=diamond, label="ImageProc OK?"]
@ -191,7 +191,7 @@ Create tests with simple shapes (square, circle, letter O with counter).
Run: go test ./pkg/dttf/tracer/... -run TestDecompose
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_tracer_phase1 [
@ -206,7 +206,7 @@ Run:
3. Verify closed paths return to origin
Write results to .ai/verify_tracer_phase1.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_tracer_phase1 [shape=diamond, label="Phase1 OK?"]
@ -230,7 +230,7 @@ Create tests comparing input/output vertex counts.
Run: go test ./pkg/dttf/tracer/... -run TestPolygon
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_tracer_phase2 [
@ -245,7 +245,7 @@ Run:
3. Verify shape is preserved
Write results to .ai/verify_tracer_phase2.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_tracer_phase2 [shape=diamond, label="Phase2 OK?"]
@ -270,7 +270,7 @@ Create tests with known curves.
Run: go test ./pkg/dttf/tracer/... -run TestBezier
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_tracer_phase3 [
@ -285,7 +285,7 @@ Run:
3. Verify curves are quadratic (TrueType format)
Write results to .ai/verify_tracer_phase3.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_tracer_phase3 [shape=diamond, label="Phase3 OK?"]
@ -310,7 +310,7 @@ Create tests validating each constraint.
Run: go test ./pkg/dttf/tracer/... -run TestOptimize
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_tracer_phase4 [
@ -326,7 +326,7 @@ Run:
4. Check point count cap
Write results to .ai/verify_tracer_phase4.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_tracer_phase4 [shape=diamond, label="Phase4 OK?"]
@ -348,7 +348,7 @@ Create tests with known transformations.
Run: go test ./pkg/dttf/tracer/... -run TestCoordinates
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_coordinates [
@ -363,7 +363,7 @@ Run:
3. Verify integer output
Write results to .ai/verify_coordinates.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_coordinates [shape=diamond, label="Coords OK?"]
@ -387,7 +387,7 @@ Create integration tests.
Run: go test ./pkg/dttf/... -run TestTrace
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_tracer_main [
@ -402,7 +402,7 @@ Run:
3. Verify all phases are called
Write results to .ai/verify_tracer_main.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_tracer_main [shape=diamond, label="Tracer OK?"]
@ -424,7 +424,7 @@ Create tests with known glyph shapes.
Run: go test ./pkg/dttf/... -run TestMetrics
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_metrics [
@ -439,7 +439,7 @@ Run:
3. Verify sidebearing calculations
Write results to .ai/verify_metrics.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_metrics [shape=diamond, label="Metrics OK?"]
@ -471,7 +471,7 @@ Create tests validating table structure.
Run: go test ./pkg/dttf/ttf/... -v
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_tables [
@ -486,7 +486,7 @@ Run:
3. Verify checksums are computed correctly
Write results to .ai/verify_tables.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_tables [shape=diamond, label="Tables OK?"]
@ -509,7 +509,7 @@ Implement in pkg/dttf/assembler.go:
Run: go test ./pkg/dttf/... -run TestAssemble
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_assembler [
@ -524,7 +524,7 @@ Run:
3. Verify offset calculations
Write results to .ai/verify_assembler.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_assembler [shape=diamond, label="Assembler OK?"]
@ -546,7 +546,7 @@ Create tests writing to temp files.
Run: go test ./pkg/dttf/... -run TestWrite
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_writer [
@ -561,7 +561,7 @@ Run:
3. Verify checksum calculation
Write results to .ai/verify_writer.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_writer [shape=diamond, label="Writer OK?"]
@ -582,7 +582,7 @@ Create end-to-end test with sample input directory.
Run: go test ./pkg/dttf/... -run TestBuild
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_pipeline [
@ -597,7 +597,7 @@ Run:
3. Verify parallelization works
Write results to .ai/verify_pipeline.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_pipeline [shape=diamond, label="Pipeline OK?"]
@ -622,7 +622,7 @@ Create tests with valid and invalid fonts.
Run: go test ./pkg/dttf/... -run TestValidate
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_validator [
@ -637,7 +637,7 @@ Run:
3. Check that invalid fonts fail appropriately
Write results to .ai/verify_validator.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_validator [shape=diamond, label="Validator OK?"]
@ -662,7 +662,7 @@ Create tests with a simple font.
Run: go test ./pkg/dttf/... -run TestRasterize
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_rasterizer [
@ -677,7 +677,7 @@ Run:
3. Verify font.json is written
Write results to .ai/verify_rasterizer.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_rasterizer [shape=diamond, label="Rasterizer OK?"]
@ -699,7 +699,7 @@ Create tests with identical/different images.
Run: go test ./pkg/dttf/quality/... -run TestSSIM
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_ssim [
@ -714,7 +714,7 @@ Run:
3. Check different images return < 1.0
Write results to .ai/verify_ssim.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_ssim [shape=diamond, label="SSIM OK?"]
@ -738,7 +738,7 @@ Create tests with a known font.
Run: go test ./pkg/dttf/test/... -run TestHarness
Write status.json: outcome=success if tests pass, outcome=fail otherwise."
Write status.json: outcome=succeeded if tests pass, outcome=failed otherwise."
]
verify_test_harness [
@ -753,7 +753,7 @@ Run:
3. Verify SSIM scores are computed
Write results to .ai/verify_test_harness.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_test_harness [shape=diamond, label="Harness OK?"]
@ -774,7 +774,7 @@ Update cmd/dttf/main.go to register command.
Run: go build ./cmd/dttf && ./cmd/dttf build --help
Write status.json: outcome=success if help displays, outcome=fail otherwise."
Write status.json: outcome=succeeded if help displays, outcome=failed otherwise."
]
verify_cli_build [
@ -789,7 +789,7 @@ Run:
3. Check all flags are present
Write results to .ai/verify_cli_build.md.
Write status.json: outcome=success if help works, outcome=fail with details."
Write status.json: outcome=succeeded if help works, outcome=failed with details."
]
check_cli_build [shape=diamond, label="CLI Build OK?"]
@ -810,7 +810,7 @@ Update cmd/dttf/main.go to register command.
Run: go build ./cmd/dttf && ./cmd/dttf rasterize --help
Write status.json: outcome=success if help displays, outcome=fail otherwise."
Write status.json: outcome=succeeded if help displays, outcome=failed otherwise."
]
verify_cli_rasterize [
@ -825,7 +825,7 @@ Run:
3. Check all flags are present
Write results to .ai/verify_cli_rasterize.md.
Write status.json: outcome=success if help works, outcome=fail with details."
Write status.json: outcome=succeeded if help works, outcome=failed with details."
]
check_cli_rasterize [shape=diamond, label="CLI Rasterize OK?"]
@ -846,7 +846,7 @@ Update cmd/dttf/main.go to register command.
Run: go build ./cmd/dttf && ./cmd/dttf validate --help
Write status.json: outcome=success if help displays, outcome=fail otherwise."
Write status.json: outcome=succeeded if help displays, outcome=failed otherwise."
]
verify_cli_validate [
@ -860,7 +860,7 @@ Run:
2. ./cmd/dttf validate --help
Write results to .ai/verify_cli_validate.md.
Write status.json: outcome=success if help works, outcome=fail with details."
Write status.json: outcome=succeeded if help works, outcome=failed with details."
]
check_cli_validate [shape=diamond, label="CLI Validate OK?"]
@ -882,7 +882,7 @@ Update cmd/dttf/main.go to register command.
Run: go build ./cmd/dttf && ./cmd/dttf test --help
Write status.json: outcome=success if help displays, outcome=fail otherwise."
Write status.json: outcome=succeeded if help displays, outcome=failed otherwise."
]
verify_cli_test [
@ -897,7 +897,7 @@ Run:
3. Check all flags are present
Write results to .ai/verify_cli_test.md.
Write status.json: outcome=success if help works, outcome=fail with details."
Write status.json: outcome=succeeded if help works, outcome=failed with details."
]
check_cli_test [shape=diamond, label="CLI Test OK?"]
@ -923,7 +923,7 @@ This is an end-to-end test of the full pipeline.
Run: go test ./test/... -v -timeout 5m
Write status.json: outcome=success if all steps pass and SSIM > 0.90, outcome=fail with details."
Write status.json: outcome=succeeded if all steps pass and SSIM > 0.90, outcome=failed with details."
]
verify_integration [
@ -939,7 +939,7 @@ Run:
4. Check SSIM threshold met
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if all pass, outcome=fail with details."
Write status.json: outcome=succeeded if all pass, outcome=failed with details."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -967,109 +967,109 @@ Run:
3. go vet ./...
Write a review report to .ai/review.md.
Write status.json: outcome=success if complete and correct, outcome=fail with missing/broken items."
Write status.json: outcome=succeeded if complete and correct, outcome=failed with missing/broken items."
]
check_review [shape=diamond, label="Review OK?"]
// Flow
start -> impl_setup -> verify_setup -> check_setup
check_setup -> impl_types [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_types [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_types -> verify_types -> check_types
check_types -> impl_loader [condition="outcome=success"]
check_types -> impl_types [condition="outcome=fail", label="retry"]
check_types -> impl_loader [condition="outcome=succeeded"]
check_types -> impl_types [condition="outcome=failed", label="retry"]
impl_loader -> verify_loader -> check_loader
check_loader -> impl_imageproc [condition="outcome=success"]
check_loader -> impl_loader [condition="outcome=fail", label="retry"]
check_loader -> impl_imageproc [condition="outcome=succeeded"]
check_loader -> impl_loader [condition="outcome=failed", label="retry"]
impl_imageproc -> verify_imageproc -> check_imageproc
check_imageproc -> impl_tracer_phase1 [condition="outcome=success"]
check_imageproc -> impl_imageproc [condition="outcome=fail", label="retry"]
check_imageproc -> impl_tracer_phase1 [condition="outcome=succeeded"]
check_imageproc -> impl_imageproc [condition="outcome=failed", label="retry"]
impl_tracer_phase1 -> verify_tracer_phase1 -> check_tracer_phase1
check_tracer_phase1 -> impl_tracer_phase2 [condition="outcome=success"]
check_tracer_phase1 -> impl_tracer_phase1 [condition="outcome=fail", label="retry"]
check_tracer_phase1 -> impl_tracer_phase2 [condition="outcome=succeeded"]
check_tracer_phase1 -> impl_tracer_phase1 [condition="outcome=failed", label="retry"]
impl_tracer_phase2 -> verify_tracer_phase2 -> check_tracer_phase2
check_tracer_phase2 -> impl_tracer_phase3 [condition="outcome=success"]
check_tracer_phase2 -> impl_tracer_phase2 [condition="outcome=fail", label="retry"]
check_tracer_phase2 -> impl_tracer_phase3 [condition="outcome=succeeded"]
check_tracer_phase2 -> impl_tracer_phase2 [condition="outcome=failed", label="retry"]
impl_tracer_phase3 -> verify_tracer_phase3 -> check_tracer_phase3
check_tracer_phase3 -> impl_tracer_phase4 [condition="outcome=success"]
check_tracer_phase3 -> impl_tracer_phase3 [condition="outcome=fail", label="retry"]
check_tracer_phase3 -> impl_tracer_phase4 [condition="outcome=succeeded"]
check_tracer_phase3 -> impl_tracer_phase3 [condition="outcome=failed", label="retry"]
impl_tracer_phase4 -> verify_tracer_phase4 -> check_tracer_phase4
check_tracer_phase4 -> impl_coordinates [condition="outcome=success"]
check_tracer_phase4 -> impl_tracer_phase4 [condition="outcome=fail", label="retry"]
check_tracer_phase4 -> impl_coordinates [condition="outcome=succeeded"]
check_tracer_phase4 -> impl_tracer_phase4 [condition="outcome=failed", label="retry"]
impl_coordinates -> verify_coordinates -> check_coordinates
check_coordinates -> impl_tracer_main [condition="outcome=success"]
check_coordinates -> impl_coordinates [condition="outcome=fail", label="retry"]
check_coordinates -> impl_tracer_main [condition="outcome=succeeded"]
check_coordinates -> impl_coordinates [condition="outcome=failed", label="retry"]
impl_tracer_main -> verify_tracer_main -> check_tracer_main
check_tracer_main -> impl_metrics [condition="outcome=success"]
check_tracer_main -> impl_tracer_main [condition="outcome=fail", label="retry"]
check_tracer_main -> impl_metrics [condition="outcome=succeeded"]
check_tracer_main -> impl_tracer_main [condition="outcome=failed", label="retry"]
impl_metrics -> verify_metrics -> check_metrics
check_metrics -> impl_tables [condition="outcome=success"]
check_metrics -> impl_metrics [condition="outcome=fail", label="retry"]
check_metrics -> impl_tables [condition="outcome=succeeded"]
check_metrics -> impl_metrics [condition="outcome=failed", label="retry"]
impl_tables -> verify_tables -> check_tables
check_tables -> impl_assembler [condition="outcome=success"]
check_tables -> impl_tables [condition="outcome=fail", label="retry"]
check_tables -> impl_assembler [condition="outcome=succeeded"]
check_tables -> impl_tables [condition="outcome=failed", label="retry"]
impl_assembler -> verify_assembler -> check_assembler
check_assembler -> impl_writer [condition="outcome=success"]
check_assembler -> impl_assembler [condition="outcome=fail", label="retry"]
check_assembler -> impl_writer [condition="outcome=succeeded"]
check_assembler -> impl_assembler [condition="outcome=failed", label="retry"]
impl_writer -> verify_writer -> check_writer
check_writer -> impl_pipeline [condition="outcome=success"]
check_writer -> impl_writer [condition="outcome=fail", label="retry"]
check_writer -> impl_pipeline [condition="outcome=succeeded"]
check_writer -> impl_writer [condition="outcome=failed", label="retry"]
impl_pipeline -> verify_pipeline -> check_pipeline
check_pipeline -> impl_validator [condition="outcome=success"]
check_pipeline -> impl_pipeline [condition="outcome=fail", label="retry"]
check_pipeline -> impl_validator [condition="outcome=succeeded"]
check_pipeline -> impl_pipeline [condition="outcome=failed", label="retry"]
impl_validator -> verify_validator -> check_validator
check_validator -> impl_rasterizer [condition="outcome=success"]
check_validator -> impl_validator [condition="outcome=fail", label="retry"]
check_validator -> impl_rasterizer [condition="outcome=succeeded"]
check_validator -> impl_validator [condition="outcome=failed", label="retry"]
impl_rasterizer -> verify_rasterizer -> check_rasterizer
check_rasterizer -> impl_ssim [condition="outcome=success"]
check_rasterizer -> impl_rasterizer [condition="outcome=fail", label="retry"]
check_rasterizer -> impl_ssim [condition="outcome=succeeded"]
check_rasterizer -> impl_rasterizer [condition="outcome=failed", label="retry"]
impl_ssim -> verify_ssim -> check_ssim
check_ssim -> impl_test_harness [condition="outcome=success"]
check_ssim -> impl_ssim [condition="outcome=fail", label="retry"]
check_ssim -> impl_test_harness [condition="outcome=succeeded"]
check_ssim -> impl_ssim [condition="outcome=failed", label="retry"]
impl_test_harness -> verify_test_harness -> check_test_harness
check_test_harness -> impl_cli_build [condition="outcome=success"]
check_test_harness -> impl_test_harness [condition="outcome=fail", label="retry"]
check_test_harness -> impl_cli_build [condition="outcome=succeeded"]
check_test_harness -> impl_test_harness [condition="outcome=failed", label="retry"]
impl_cli_build -> verify_cli_build -> check_cli_build
check_cli_build -> impl_cli_rasterize [condition="outcome=success"]
check_cli_build -> impl_cli_build [condition="outcome=fail", label="retry"]
check_cli_build -> impl_cli_rasterize [condition="outcome=succeeded"]
check_cli_build -> impl_cli_build [condition="outcome=failed", label="retry"]
impl_cli_rasterize -> verify_cli_rasterize -> check_cli_rasterize
check_cli_rasterize -> impl_cli_validate [condition="outcome=success"]
check_cli_rasterize -> impl_cli_rasterize [condition="outcome=fail", label="retry"]
check_cli_rasterize -> impl_cli_validate [condition="outcome=succeeded"]
check_cli_rasterize -> impl_cli_rasterize [condition="outcome=failed", label="retry"]
impl_cli_validate -> verify_cli_validate -> check_cli_validate
check_cli_validate -> impl_cli_test [condition="outcome=success"]
check_cli_validate -> impl_cli_validate [condition="outcome=fail", label="retry"]
check_cli_validate -> impl_cli_test [condition="outcome=succeeded"]
check_cli_validate -> impl_cli_validate [condition="outcome=failed", label="retry"]
impl_cli_test -> verify_cli_test -> check_cli_test
check_cli_test -> impl_integration [condition="outcome=success"]
check_cli_test -> impl_cli_test [condition="outcome=fail", label="retry"]
check_cli_test -> impl_integration [condition="outcome=succeeded"]
check_cli_test -> impl_cli_test [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_setup [condition="outcome=fail", label="fix from start"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_setup [condition="outcome=failed", label="fix from start"]
}

View file

@ -36,7 +36,7 @@ Expand this into a detailed spec covering:
Write the expanded spec to .ai/spec.md.
Write status.json: outcome=success"
Write status.json: outcome=succeeded"
]
// Phase 2: Project setup
@ -56,7 +56,7 @@ Create the Go project structure:
Run: go build ./...
Write status.json: outcome=success if the project builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if the project builds, outcome=failed with failure_reason otherwise."
]
verify_setup [
@ -75,7 +75,7 @@ Check that:
- pkg/ directories are created
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if all checks pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all checks pass, outcome=failed with details otherwise."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -104,7 +104,7 @@ Include comprehensive documentation for all exported types.
Run: go build ./...
Write status.json: outcome=success if builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if builds, outcome=failed with failure_reason otherwise."
]
verify_types [
@ -124,7 +124,7 @@ Check that:
- No compilation errors
Write results to .ai/verify_types.md.
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with details otherwise."
]
check_types [shape=diamond, label="Types OK?"]
@ -158,7 +158,7 @@ Read: pkg/crawler/types.go for interfaces.
Run: go test ./pkg/robotstxt/...
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_robotstxt [
@ -176,7 +176,7 @@ Check test coverage:
4. go test ./pkg/robotstxt/... -cover
Write results to .ai/verify_robotstxt.md.
Write status.json: outcome=success if all pass and coverage > 70%, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass and coverage > 70%, outcome=failed with details otherwise."
]
check_robotstxt [shape=diamond, label="Robots.txt OK?"]
@ -206,7 +206,7 @@ Read: pkg/crawler/types.go for types.
Run: go test ./pkg/checker/...
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_checker [
@ -225,7 +225,7 @@ Check:
- No race conditions: go test ./pkg/checker/... -race
Write results to .ai/verify_checker.md.
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with details otherwise."
]
check_checker [shape=diamond, label="Checker OK?"]
@ -255,7 +255,7 @@ Read: pkg/crawler/types.go for types.
Run: go test ./pkg/crawler/...
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_extractor [
@ -275,7 +275,7 @@ Check:
- Non-http schemes are filtered
Write results to .ai/verify_extractor.md.
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with details otherwise."
]
check_extractor [shape=diamond, label="Extractor OK?"]
@ -319,7 +319,7 @@ Read: pkg/crawler/types.go, pkg/crawler/extractor.go, pkg/robotstxt/, pkg/checke
Run: go test ./pkg/crawler/...
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_crawler [
@ -341,7 +341,7 @@ Check:
- No race conditions
Write results to .ai/verify_crawler.md.
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with details otherwise."
]
check_crawler [shape=diamond, label="Crawler OK?"]
@ -377,7 +377,7 @@ Read: pkg/report/types.go for types.
Run: go test ./pkg/report/...
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_report [
@ -397,7 +397,7 @@ Check:
- All test cases pass
Write results to .ai/verify_report.md.
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with details otherwise."
]
check_report [shape=diamond, label="Report OK?"]
@ -430,7 +430,7 @@ Read: all pkg/ directories for integration.
Run: go build ./cmd/linkcheck
Write status.json: outcome=success if builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if builds, outcome=failed with failure_reason otherwise."
]
verify_cli [
@ -454,7 +454,7 @@ Check:
- Exit codes are correct
Write results to .ai/verify_cli.md.
Write status.json: outcome=success if all manual tests pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all manual tests pass, outcome=failed with details otherwise."
]
check_cli [shape=diamond, label="CLI OK?"]
@ -479,7 +479,7 @@ Create integration tests in test/:
Run: go test ./test/...
Write status.json: outcome=success if integration tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if integration tests pass, outcome=failed with failure_reason otherwise."
]
verify_integration [
@ -500,7 +500,7 @@ Check:
- Project builds cleanly
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if all pass, outcome=fail with details otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with details otherwise."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -543,7 +543,7 @@ Perform final review of the complete linkcheck implementation:
- go build ./...
Write detailed review to .ai/final_review.md.
Write status.json: outcome=success if complete and working, outcome=fail with specific issues that need fixing."
Write status.json: outcome=succeeded if complete and working, outcome=failed with specific issues that need fixing."
]
check_review [shape=diamond, label="Review OK?"]
@ -551,42 +551,42 @@ Write status.json: outcome=success if complete and working, outcome=fail with sp
// Graph flow
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
check_setup -> impl_types [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_types [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_types -> verify_types -> check_types
check_types -> impl_robotstxt [condition="outcome=success"]
check_types -> impl_types [condition="outcome=fail", label="retry"]
check_types -> impl_robotstxt [condition="outcome=succeeded"]
check_types -> impl_types [condition="outcome=failed", label="retry"]
impl_robotstxt -> verify_robotstxt -> check_robotstxt
check_robotstxt -> impl_checker [condition="outcome=success"]
check_robotstxt -> impl_robotstxt [condition="outcome=fail", label="retry"]
check_robotstxt -> impl_checker [condition="outcome=succeeded"]
check_robotstxt -> impl_robotstxt [condition="outcome=failed", label="retry"]
impl_checker -> verify_checker -> check_checker
check_checker -> impl_extractor [condition="outcome=success"]
check_checker -> impl_checker [condition="outcome=fail", label="retry"]
check_checker -> impl_extractor [condition="outcome=succeeded"]
check_checker -> impl_checker [condition="outcome=failed", label="retry"]
impl_extractor -> verify_extractor -> check_extractor
check_extractor -> impl_crawler [condition="outcome=success"]
check_extractor -> impl_extractor [condition="outcome=fail", label="retry"]
check_extractor -> impl_crawler [condition="outcome=succeeded"]
check_extractor -> impl_extractor [condition="outcome=failed", label="retry"]
impl_crawler -> verify_crawler -> check_crawler
check_crawler -> impl_report [condition="outcome=success"]
check_crawler -> impl_crawler [condition="outcome=fail", label="retry"]
check_crawler -> impl_report [condition="outcome=succeeded"]
check_crawler -> impl_crawler [condition="outcome=failed", label="retry"]
impl_report -> verify_report -> check_report
check_report -> impl_cli [condition="outcome=success"]
check_report -> impl_report [condition="outcome=fail", label="retry"]
check_report -> impl_cli [condition="outcome=succeeded"]
check_report -> impl_report [condition="outcome=failed", label="retry"]
impl_cli -> verify_cli -> check_cli
check_cli -> impl_integration [condition="outcome=success"]
check_cli -> impl_cli [condition="outcome=fail", label="retry"]
check_cli -> impl_integration [condition="outcome=succeeded"]
check_cli -> impl_cli [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_cli [condition="outcome=fail", label="fix"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_cli [condition="outcome=failed", label="fix"]
}

View file

@ -31,7 +31,7 @@ Create:
Run: go build ./...
Write status.json: outcome=success if project builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if project builds, outcome=failed with failure_reason otherwise."
]
verify_setup [
@ -46,7 +46,7 @@ Run:
3. Check that all required directories exist
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -77,7 +77,7 @@ Acceptance:
- go build ./...
- go test ./pkg/game/
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_core_types [
@ -93,7 +93,7 @@ Run:
4. Check test coverage: go test -cover ./pkg/game/
Write results to .ai/verify_core_types.md.
Write status.json: outcome=success if ALL pass and coverage >70%, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass and coverage >70%, outcome=failed with details."
]
check_core_types [shape=diamond, label="Types OK?"]
@ -140,7 +140,7 @@ Acceptance:
- go build ./...
- go test ./pkg/game/ -v (all tests pass)
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_game_logic [
@ -156,7 +156,7 @@ Run:
4. go test -cover ./pkg/game/ (check for >80% coverage)
Write results to .ai/verify_game_logic.md.
Write status.json: outcome=success if ALL pass with good coverage, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass with good coverage, outcome=failed with details."
]
check_game_logic [shape=diamond, label="Logic OK?"]
@ -194,7 +194,7 @@ Acceptance:
- go build ./...
- go test ./pkg/ui/
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_ui_render [
@ -210,7 +210,7 @@ Run:
4. Check that bubbletea dependency is properly added to go.mod
Write results to .ai/verify_ui_render.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_ui_render [shape=diamond, label="Render OK?"]
@ -255,7 +255,7 @@ Acceptance:
- Binary runs and displays game (manual check)
- All keyboard controls work (manual check)
Write status.json: outcome=success if builds and runs, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if builds and runs, outcome=failed with failure_reason otherwise."
]
verify_ui_input [
@ -272,7 +272,7 @@ Run:
5. Check that binary exists at ./cmd/solitaire/solitaire
Write results to .ai/verify_ui_input.md.
Write status.json: outcome=success if ALL pass and binary exists, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass and binary exists, outcome=failed with details."
]
check_ui_input [shape=diamond, label="Input OK?"]
@ -312,7 +312,7 @@ Acceptance:
- go test ./pkg/storage/
- Manual test: save game, quit, restart with --load
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_persistence [
@ -328,7 +328,7 @@ Run:
4. go test ./... (all tests)
Write results to .ai/verify_persistence.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_persistence [shape=diamond, label="Persist OK?"]
@ -368,7 +368,7 @@ Acceptance:
- All keyboard commands work
- Game is winnable
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_integration [
@ -387,7 +387,7 @@ Run:
7. Verify README exists and has instructions
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -425,7 +425,7 @@ Verify:
Run full test suite and manual gameplay check.
Write comprehensive review to .ai/final_review.md.
Write status.json: outcome=success if COMPLETE per spec, outcome=fail with specific missing items."
Write status.json: outcome=succeeded if COMPLETE per spec, outcome=failed with specific missing items."
]
check_review [shape=diamond, label="Review OK?"]
@ -434,40 +434,40 @@ Write status.json: outcome=success if COMPLETE per spec, outcome=fail with speci
start -> impl_setup
impl_setup -> verify_setup
verify_setup -> check_setup
check_setup -> impl_core_types [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_core_types [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_core_types -> verify_core_types
verify_core_types -> check_core_types
check_core_types -> impl_game_logic [condition="outcome=success"]
check_core_types -> impl_core_types [condition="outcome=fail", label="retry"]
check_core_types -> impl_game_logic [condition="outcome=succeeded"]
check_core_types -> impl_core_types [condition="outcome=failed", label="retry"]
impl_game_logic -> verify_game_logic
verify_game_logic -> check_game_logic
check_game_logic -> impl_ui_render [condition="outcome=success"]
check_game_logic -> impl_game_logic [condition="outcome=fail", label="retry"]
check_game_logic -> impl_ui_render [condition="outcome=succeeded"]
check_game_logic -> impl_game_logic [condition="outcome=failed", label="retry"]
impl_ui_render -> verify_ui_render
verify_ui_render -> check_ui_render
check_ui_render -> impl_ui_input [condition="outcome=success"]
check_ui_render -> impl_ui_render [condition="outcome=fail", label="retry"]
check_ui_render -> impl_ui_input [condition="outcome=succeeded"]
check_ui_render -> impl_ui_render [condition="outcome=failed", label="retry"]
impl_ui_input -> verify_ui_input
verify_ui_input -> check_ui_input
check_ui_input -> impl_persistence [condition="outcome=success"]
check_ui_input -> impl_ui_input [condition="outcome=fail", label="retry"]
check_ui_input -> impl_persistence [condition="outcome=succeeded"]
check_ui_input -> impl_ui_input [condition="outcome=failed", label="retry"]
impl_persistence -> verify_persistence
verify_persistence -> check_persistence
check_persistence -> impl_integration [condition="outcome=success"]
check_persistence -> impl_persistence [condition="outcome=fail", label="retry"]
check_persistence -> impl_integration [condition="outcome=succeeded"]
check_persistence -> impl_persistence [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration
verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_integration [condition="outcome=fail", label="fix"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_integration [condition="outcome=failed", label="fix"]
}

View file

@ -31,7 +31,7 @@ Create Go project structure:
Run: go build ./...
Write status.json: outcome=success if builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if builds, outcome=failed with failure_reason otherwise."
]
verify_setup [
@ -45,7 +45,7 @@ Run:
3. Check that pkg/dttf/types.go exists and contains all required types from specs/dttf-v1.md section 7.1
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -71,7 +71,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_loader [
@ -85,7 +85,7 @@ Run:
3. go test ./pkg/dttf/... -v -run Loader
Write results to .ai/verify_loader.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_loader [shape=diamond, label="Loader OK?"]
@ -115,7 +115,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v -run Tracer
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_tracer [
@ -131,7 +131,7 @@ Run:
5. Verify winding direction enforcement (section 3.2 Phase 4)
Write results to .ai/verify_tracer.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_tracer [shape=diamond, label="Tracer OK?"]
@ -155,7 +155,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v -run Metrics
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_metrics [
@ -169,7 +169,7 @@ Run:
3. go test ./pkg/dttf/... -v -run Metrics
Write results to .ai/verify_metrics.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_metrics [shape=diamond, label="Metrics OK?"]
@ -199,7 +199,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v -run Tables
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_tables [
@ -215,7 +215,7 @@ Run:
5. Verify checksums are computed correctly
Write results to .ai/verify_tables.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_tables [shape=diamond, label="Tables OK?"]
@ -246,7 +246,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_writer [
@ -261,7 +261,7 @@ Run:
4. go test ./pkg/dttf/... -v -run Build
Write results to .ai/verify_writer.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_writer [shape=diamond, label="Writer OK?"]
@ -288,7 +288,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v -run Validator
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_validator [
@ -302,7 +302,7 @@ Run:
3. go test ./pkg/dttf/... -v -run Validator
Write results to .ai/verify_validator.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_validator [shape=diamond, label="Validator OK?"]
@ -329,7 +329,7 @@ Acceptance:
- go build ./...
- go test ./pkg/dttf/... -v -run Rasterizer
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_rasterizer [
@ -343,7 +343,7 @@ Run:
3. go test ./pkg/dttf/... -v -run Rasterizer
Write results to .ai/verify_rasterizer.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_rasterizer [shape=diamond, label="Rasterizer OK?"]
@ -369,7 +369,7 @@ Acceptance:
- ./cmd/dttf/dttf --help
- Test each command with --help
Write status.json: outcome=success if all commands work, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all commands work, outcome=failed with failure_reason otherwise."
]
verify_cli [
@ -387,7 +387,7 @@ Run:
7. ./cmd/dttf/dttf test --help
Write results to .ai/verify_cli.md.
Write status.json: outcome=success if ALL commands exist and show help, outcome=fail with details."
Write status.json: outcome=succeeded if ALL commands exist and show help, outcome=failed with details."
]
check_cli [shape=diamond, label="CLI OK?"]
@ -420,7 +420,7 @@ Acceptance:
- go test ./pkg/dttf/... -v -run TestHarness
- dttf test --help shows correct options
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_test_harness [
@ -436,7 +436,7 @@ Run:
5. Verify reference font download works
Write results to .ai/verify_test_harness.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_test_harness [shape=diamond, label="Test Harness OK?"]
@ -463,7 +463,7 @@ Acceptance:
- Output font is valid
- SSIM > 0.90 (acceptable threshold from section 5.2)
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason and specific metrics otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason and specific metrics otherwise."
]
verify_integration [
@ -478,7 +478,7 @@ Run:
4. go test ./... (full test suite)
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -513,57 +513,57 @@ Run:
- dttf test --reference-dir <test-fonts>/ if available
Write comprehensive review to .ai/final_review.md.
Write status.json: outcome=success if complete and spec-compliant, outcome=fail with specific missing/incorrect features otherwise."
Write status.json: outcome=succeeded if complete and spec-compliant, outcome=failed with specific missing/incorrect features otherwise."
]
check_review [shape=diamond, label="Review OK?"]
// Flow
start -> impl_setup -> verify_setup -> check_setup
check_setup -> impl_loader [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_loader [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_loader -> verify_loader -> check_loader
check_loader -> impl_tracer [condition="outcome=success"]
check_loader -> impl_loader [condition="outcome=fail", label="retry"]
check_loader -> impl_tracer [condition="outcome=succeeded"]
check_loader -> impl_loader [condition="outcome=failed", label="retry"]
impl_tracer -> verify_tracer -> check_tracer
check_tracer -> impl_metrics [condition="outcome=success"]
check_tracer -> impl_tracer [condition="outcome=fail", label="retry"]
check_tracer -> impl_metrics [condition="outcome=succeeded"]
check_tracer -> impl_tracer [condition="outcome=failed", label="retry"]
impl_metrics -> verify_metrics -> check_metrics
check_metrics -> impl_tables [condition="outcome=success"]
check_metrics -> impl_metrics [condition="outcome=fail", label="retry"]
check_metrics -> impl_tables [condition="outcome=succeeded"]
check_metrics -> impl_metrics [condition="outcome=failed", label="retry"]
impl_tables -> verify_tables -> check_tables
check_tables -> impl_writer [condition="outcome=success"]
check_tables -> impl_tables [condition="outcome=fail", label="retry"]
check_tables -> impl_writer [condition="outcome=succeeded"]
check_tables -> impl_tables [condition="outcome=failed", label="retry"]
impl_writer -> verify_writer -> check_writer
check_writer -> impl_validator [condition="outcome=success"]
check_writer -> impl_writer [condition="outcome=fail", label="retry"]
check_writer -> impl_validator [condition="outcome=succeeded"]
check_writer -> impl_writer [condition="outcome=failed", label="retry"]
impl_validator -> verify_validator -> check_validator
check_validator -> impl_rasterizer [condition="outcome=success"]
check_validator -> impl_validator [condition="outcome=fail", label="retry"]
check_validator -> impl_rasterizer [condition="outcome=succeeded"]
check_validator -> impl_validator [condition="outcome=failed", label="retry"]
impl_rasterizer -> verify_rasterizer -> check_rasterizer
check_rasterizer -> impl_cli [condition="outcome=success"]
check_rasterizer -> impl_rasterizer [condition="outcome=fail", label="retry"]
check_rasterizer -> impl_cli [condition="outcome=succeeded"]
check_rasterizer -> impl_rasterizer [condition="outcome=failed", label="retry"]
impl_cli -> verify_cli -> check_cli
check_cli -> impl_test_harness [condition="outcome=success"]
check_cli -> impl_cli [condition="outcome=fail", label="retry"]
check_cli -> impl_test_harness [condition="outcome=succeeded"]
check_cli -> impl_cli [condition="outcome=failed", label="retry"]
impl_test_harness -> verify_test_harness -> check_test_harness
check_test_harness -> impl_integration [condition="outcome=success"]
check_test_harness -> impl_test_harness [condition="outcome=fail", label="retry"]
check_test_harness -> impl_integration [condition="outcome=succeeded"]
check_test_harness -> impl_test_harness [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_integration [condition="outcome=fail", label="fix"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_integration [condition="outcome=failed", label="fix"]
}

View file

@ -37,7 +37,7 @@ Expand into a detailed spec covering:
Write the spec to .ai/spec.md.
Write status.json: outcome=success"
Write status.json: outcome=succeeded"
]
// Project setup
@ -56,7 +56,7 @@ Acceptance:
- `go build ./...` must pass
- Directory structure matches spec
Write status.json: outcome=success if all pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason otherwise."
]
verify_setup [
@ -72,7 +72,7 @@ Run:
5. Check that pkg/ subdirectories exist
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -104,7 +104,7 @@ Acceptance:
- `go build ./...` must pass
- `go test ./pkg/crawler/...` must pass with all tests
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_crawler [
@ -120,7 +120,7 @@ Run:
5. Check that tests cover FetchPage and ExtractLinks
Write results to .ai/verify_crawler.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_crawler [shape=diamond, label="Crawler OK?"]
@ -148,7 +148,7 @@ Acceptance:
- `go build ./...` must pass
- `go test ./pkg/robots/...` must pass
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_robots [
@ -163,7 +163,7 @@ Run:
4. Check that pkg/robots/robots.go exists
Write results to .ai/verify_robots.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_robots [shape=diamond, label="Robots OK?"]
@ -194,7 +194,7 @@ Acceptance:
- `go build ./...` must pass
- `go test ./pkg/checker/...` must pass
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_checker [
@ -209,7 +209,7 @@ Run:
4. Check that pkg/checker/checker.go exists
Write results to .ai/verify_checker.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_checker [shape=diamond, label="Checker OK?"]
@ -238,7 +238,7 @@ Acceptance:
- `go build ./...` must pass
- `go test ./pkg/formatter/...` must pass
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_formatter [
@ -253,7 +253,7 @@ Run:
4. Check that both text.go and json.go exist
Write results to .ai/verify_formatter.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_formatter [shape=diamond, label="Formatter OK?"]
@ -278,7 +278,7 @@ Acceptance:
- `./linkcheck --help` shows usage
- `./linkcheck https://example.com` runs without error
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_cli [
@ -293,7 +293,7 @@ Run:
4. Check that main.go wires all packages together
Write results to .ai/verify_cli.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_cli [shape=diamond, label="CLI OK?"]
@ -318,7 +318,7 @@ Acceptance:
- `go test ./...` must pass (all tests)
- Integration test validates end-to-end functionality
Write status.json: outcome=success if all criteria pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
]
verify_integration [
@ -332,7 +332,7 @@ Run:
3. Check that integration test covers depth, format, robots.txt
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if ALL pass, outcome=fail with details."
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -360,41 +360,41 @@ Run:
3. Manual test: `./linkcheck https://example.com --depth 1 --format json`
Write review to .ai/final_review.md.
Write status.json: outcome=success if complete and correct, outcome=fail with what's missing or broken."
Write status.json: outcome=succeeded if complete and correct, outcome=failed with what's missing or broken."
]
check_review [shape=diamond, label="Review OK?"]
// Flow
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
check_setup -> impl_crawler [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_crawler [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_crawler -> verify_crawler -> check_crawler
check_crawler -> impl_robots [condition="outcome=success"]
check_crawler -> impl_crawler [condition="outcome=fail", label="retry"]
check_crawler -> impl_robots [condition="outcome=succeeded"]
check_crawler -> impl_crawler [condition="outcome=failed", label="retry"]
impl_robots -> verify_robots -> check_robots
check_robots -> impl_checker [condition="outcome=success"]
check_robots -> impl_robots [condition="outcome=fail", label="retry"]
check_robots -> impl_checker [condition="outcome=succeeded"]
check_robots -> impl_robots [condition="outcome=failed", label="retry"]
impl_checker -> verify_checker -> check_checker
check_checker -> impl_formatter [condition="outcome=success"]
check_checker -> impl_checker [condition="outcome=fail", label="retry"]
check_checker -> impl_formatter [condition="outcome=succeeded"]
check_checker -> impl_checker [condition="outcome=failed", label="retry"]
impl_formatter -> verify_formatter -> check_formatter
check_formatter -> impl_cli [condition="outcome=success"]
check_formatter -> impl_formatter [condition="outcome=fail", label="retry"]
check_formatter -> impl_cli [condition="outcome=succeeded"]
check_formatter -> impl_formatter [condition="outcome=failed", label="retry"]
impl_cli -> verify_cli -> check_cli
check_cli -> impl_integration [condition="outcome=success"]
check_cli -> impl_cli [condition="outcome=fail", label="retry"]
check_cli -> impl_integration [condition="outcome=succeeded"]
check_cli -> impl_cli [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_integration [condition="outcome=fail", label="fix"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_integration [condition="outcome=failed", label="fix"]
}

View file

@ -30,7 +30,7 @@ Expand into a detailed spec covering:
Write the spec to .ai/spec.md.
Write status.json: outcome=success"
Write status.json: outcome=succeeded"
]
// Project setup
@ -50,7 +50,7 @@ Run the appropriate build command:
- Go: go build ./...
- Rust: cargo build
Write status.json: outcome=success if project structure is created and builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if project structure is created and builds, outcome=failed with failure_reason otherwise."
]
verify_setup [
@ -68,7 +68,7 @@ Run:
4. Verify no syntax errors
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if ALL checks pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if ALL checks pass, outcome=failed with failure_reason otherwise."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -93,7 +93,7 @@ Run appropriate test command:
- Go: go test ./...
- Rust: cargo test
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_data_structures [
@ -114,7 +114,7 @@ Run:
4. Verify basic operations work (create deck, shuffle, deal)
Write results to .ai/verify_data_structures.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_data_structures [shape=diamond, label="Data structures OK?"]
@ -146,7 +146,7 @@ Run tests for the chosen language ONLY (do not run other language commands):
- Go: go test ./... -v
- Rust: cargo test -- --nocapture
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_game_logic [
@ -168,7 +168,7 @@ Run:
5. Test undo functionality
Write results to .ai/verify_game_logic.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_game_logic [shape=diamond, label="Game logic OK?"]
@ -203,7 +203,7 @@ Run for the chosen language ONLY (do not run other language commands):
- Go: go build ./... && go test ./...
- Rust: cargo build && cargo test
Write status.json: outcome=success if builds and tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if builds and tests pass, outcome=failed with failure_reason otherwise."
]
verify_terminal_ui [
@ -225,7 +225,7 @@ Run:
5. Test that rendering doesn't crash with empty game state
Write results to .ai/verify_terminal_ui.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_terminal_ui [shape=diamond, label="Terminal UI OK?"]
@ -256,7 +256,7 @@ IMPORTANT: Run for the chosen language ONLY (do not run other language commands)
- Go: go build ./cmd/solitaire && go test ./...
- Rust: cargo build --release && cargo test
Write status.json: outcome=success if game runs and all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if game runs and all tests pass, outcome=failed with failure_reason otherwise."
]
verify_integration [
@ -278,7 +278,7 @@ Run:
5. Test that game starts without errors
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -314,33 +314,33 @@ Write detailed review to .ai/final_review.md including:
- Any issues found
- Compliance with spec
Write status.json: outcome=success if the game is complete and playable per spec, outcome=fail with what's missing or broken."
Write status.json: outcome=succeeded if the game is complete and playable per spec, outcome=failed with what's missing or broken."
]
check_review [shape=diamond, label="Review OK?"]
// Flow
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
check_setup -> impl_data_structures [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_data_structures [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
impl_data_structures -> verify_data_structures -> check_data_structures
check_data_structures -> impl_game_logic [condition="outcome=success"]
check_data_structures -> impl_data_structures [condition="outcome=fail", label="retry"]
check_data_structures -> impl_game_logic [condition="outcome=succeeded"]
check_data_structures -> impl_data_structures [condition="outcome=failed", label="retry"]
impl_game_logic -> verify_game_logic -> check_game_logic
check_game_logic -> impl_terminal_ui [condition="outcome=success"]
check_game_logic -> impl_game_logic [condition="outcome=fail", label="retry"]
check_game_logic -> impl_terminal_ui [condition="outcome=succeeded"]
check_game_logic -> impl_game_logic [condition="outcome=failed", label="retry"]
impl_terminal_ui -> verify_terminal_ui -> check_terminal_ui
check_terminal_ui -> impl_integration [condition="outcome=success"]
check_terminal_ui -> impl_terminal_ui [condition="outcome=fail", label="retry"]
check_terminal_ui -> impl_integration [condition="outcome=succeeded"]
check_terminal_ui -> impl_terminal_ui [condition="outcome=failed", label="retry"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_terminal_ui [condition="outcome=fail", label="fix"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_terminal_ui [condition="outcome=failed", label="fix"]
}

View file

@ -309,9 +309,9 @@ digraph reference_template {
// Linear start: toolchain gate -> spec -> DoD check
start -> check_toolchain
check_toolchain -> expand_spec [condition="outcome=success"]
check_toolchain -> check_toolchain [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_toolchain -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_toolchain -> expand_spec [condition="outcome=succeeded"]
check_toolchain -> check_toolchain [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_toolchain -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_toolchain -> postmortem
expand_spec -> check_dod
@ -340,42 +340,42 @@ digraph reference_template {
// Verify/check inner loop (tool gates first, semantic review last)
implement -> check_implement
check_implement -> fix_fmt [condition="outcome=success"]
check_implement -> fix_fmt [condition="outcome=succeeded"]
fix_fmt -> verify_fmt
check_implement -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_implement -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_implement -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_implement -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_implement -> postmortem
verify_fmt -> check_fmt
check_fmt -> verify_build [condition="outcome=success"]
check_fmt -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_fmt -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_fmt -> verify_build [condition="outcome=succeeded"]
check_fmt -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_fmt -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_fmt -> postmortem
verify_build -> check_build
check_build -> verify_test [condition="outcome=success"]
check_build -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_build -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_build -> verify_test [condition="outcome=succeeded"]
check_build -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_build -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_build -> postmortem
verify_test -> check_test
check_test -> verify_artifacts [condition="outcome=success"]
check_test -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_test -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_test -> verify_artifacts [condition="outcome=succeeded"]
check_test -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_test -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_test -> postmortem
verify_artifacts -> check_artifacts
check_artifacts -> verify_fidelity [condition="outcome=success"]
check_artifacts -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_artifacts -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_artifacts -> verify_fidelity [condition="outcome=succeeded"]
check_artifacts -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_artifacts -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_artifacts -> postmortem
verify_fidelity -> check_impl
check_impl -> review_fanout [condition="outcome=success"]
check_impl -> review_fanout [condition="outcome=succeeded"]
review_fanout -> review_a
review_fanout -> review_b
review_fanout -> review_c
check_impl -> implement [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_impl -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_impl -> implement [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_impl -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_impl -> postmortem
// Review fan-in -> consensus
@ -384,11 +384,11 @@ digraph reference_template {
review_c -> review_consensus
// Consensus routing: success -> exit, anything else -> postmortem
review_consensus -> exit [condition="outcome=success"]
review_consensus -> exit [condition="outcome=succeeded"]
review_consensus -> postmortem
// Domain-routed recovery: classify failure and choose the right re-entry
postmortem -> check_toolchain [condition="outcome=fail && context.failure_class=transient_infra"]
postmortem -> check_toolchain [condition="outcome=failed && context.failure_class=transient_infra"]
postmortem -> implement [condition="outcome=impl_repair"]
postmortem -> plan_fanout [condition="outcome=needs_replan"]
postmortem -> check_toolchain [condition="outcome=needs_toolchain"]

View file

@ -3,7 +3,7 @@ digraph Workflow {
FinalizeAndUpdateLedger [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="6) Finalize & update ledger", model="gpt-5.2-codex", llm_prompt="**Finalize implementation and update ledger in one step.**\\n\\n1. Synthesize the port plan from .ai/semport_plan_sonnet.md and implementation results into .ai/semport_implementation_summary.md. List which upstream commits were processed, what changes were made (with file:line references), and the disposition ('implemented').\\n\\n2. Update the ledger using:\\n```\\npython3 semport/ledger.py update <shortsha> implemented\\npython3 semport/ledger.py sort\\n```\\n\\n3. Verify with `python3 semport/ledger.py stats` to see progress.\\n\\n4. **Commit all changes** (implementation + ledger update) with a clear message:\\n ```\\n git add -A\\n git commit -m \"semport: implement <shortsha> - <brief description of what was ported>\"\\n ```\\n Example: `git commit -m \"semport: implement a776d80 - nest handoff history by default\"`\\n\\nKeep our goal $goal in mind. Then loop back to process the next commit.", margin="0.1,0.08", max_agent_turns="8", node_type="stack.observe", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
TestValidate [allow_partial="true", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="5) Test/Validate changes", model="gpt-5.2-codex", llm_prompt="Keeping our goal in mind: $goal. From repo root, validate that all ported changes work correctly. Run relevant tests (go test ./...), ensure compilation succeeds, and verify the ported functionality matches the upstream semantic intent (not necessarily syntax). Write validation results to .ai/semport_validation_report_NN.md. Use outcome=yes if all tests pass and changes are semantically correct; otherwise use outcome=retry with concrete failure details.", margin="0.1,0.08", max_agent_turns="8", node_type="stack.steer", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1800"];
TestValidate [allow_partial="true", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="5) Test/Validate changes", model="gpt-5.2-codex", llm_prompt="Keeping our goal in mind: $goal. From repo root, validate that all ported changes work correctly. Run relevant tests (go test ./...), ensure compilation succeeds, and verify the ported functionality matches the upstream semantic intent (not necessarily syntax). Write validation results to .ai/semport_validation_report_NN.md. Use outcome=succeeded if all tests pass and changes are semantically correct; otherwise use outcome=failed with concrete failure details.", margin="0.1,0.08", max_agent_turns="8", node_type="stack.steer", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1800"];
AnalyzeFailureSonnet [allow_partial="false", color="#94a3b8", fillcolor="white", fontname="Helvetica", fontsize="12", is_codergen="true", label="6a) Analyze failure (sonnet)", model="gpt-5.2-codex", llm_prompt="When tests or validation fail, inspect .ai/semport_validation_report_*.md, logs, diffs, and error messages. Write .ai/semport_failure_sonnet.md summarizing root causes, impacted files (with line references), and what needs to be fixed. Clearly note where failure artifacts are located. Keep our goal $goal in mind and be subjective.", margin="0.1,0.08", max_agent_turns="8", node_type="stack.observe", penwidth="1.2", reasoning_effort="high", shape="box", style="rounded,filled", timeout="1200"];
@ -23,8 +23,8 @@ digraph Workflow {
Start -> FetchUpstreamSonnet;
AnalyzeFailureSonnet -> FinalizeAndUpdateLedger;
FinalizePlanGPT -> ImplementPort;
TestValidate -> FinalizeAndUpdateLedger [condition="outcome=yes", label="pass"];
TestValidate -> AnalyzeFailureSonnet [condition="outcome=retry", label="fail"];
TestValidate -> FinalizeAndUpdateLedger [condition="outcome=succeeded", label="pass"];
TestValidate -> AnalyzeFailureSonnet [condition="outcome=failed", label="fail"];
FetchUpstreamSonnet -> AnalyzePlanSonnet [condition="outcome=process", label="process"];
FetchUpstreamSonnet -> Exit [condition="outcome=done", label="done"];
ImplementPort -> TestValidate;

View file

@ -30,7 +30,7 @@ Expand into a detailed spec covering:
Write the spec to .ai/spec.md.
Write status.json: outcome=success"
Write status.json: outcome=succeeded"
]
// Project setup
@ -50,7 +50,7 @@ Run the appropriate build command:
- Go: go build ./...
- Rust: cargo build
Write status.json: outcome=success if project structure is created and builds, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if project structure is created and builds, outcome=failed with failure_reason otherwise."
]
verify_setup [
@ -68,7 +68,7 @@ Run:
4. Verify no syntax errors
Write results to .ai/verify_setup.md.
Write status.json: outcome=success if ALL checks pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if ALL checks pass, outcome=failed with failure_reason otherwise."
]
check_setup [shape=diamond, label="Setup OK?"]
@ -93,7 +93,7 @@ Run appropriate test command:
- Go: go test ./...
- Rust: cargo test
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_data_structures [
@ -114,7 +114,7 @@ Run:
4. Verify basic operations work (create deck, shuffle, deal)
Write results to .ai/verify_data_structures.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_data_structures [shape=diamond, label="Data structures OK?"]
@ -146,7 +146,7 @@ Run tests for the chosen language ONLY (do not run other language commands):
- Go: go test ./... -v
- Rust: cargo test -- --nocapture
Write status.json: outcome=success if all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if all tests pass, outcome=failed with failure_reason otherwise."
]
verify_game_logic [
@ -168,7 +168,7 @@ Run:
5. Test undo functionality
Write results to .ai/verify_game_logic.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_game_logic [shape=diamond, label="Game logic OK?"]
@ -203,7 +203,7 @@ Run for the chosen language ONLY (do not run other language commands):
- Go: go build ./... && go test ./...
- Rust: cargo build && cargo test
Write status.json: outcome=success if builds and tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if builds and tests pass, outcome=failed with failure_reason otherwise."
]
verify_terminal_ui [
@ -225,7 +225,7 @@ Run:
5. Test that rendering doesn't crash with empty game state
Write results to .ai/verify_terminal_ui.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_terminal_ui [shape=diamond, label="Terminal UI OK?"]
@ -256,7 +256,7 @@ IMPORTANT: Run for the chosen language ONLY (do not run other language commands)
- Go: go build ./cmd/solitaire && go test ./...
- Rust: cargo build --release && cargo test
Write status.json: outcome=success if game runs and all tests pass, outcome=fail with failure_reason otherwise."
Write status.json: outcome=succeeded if game runs and all tests pass, outcome=failed with failure_reason otherwise."
]
verify_integration [
@ -278,7 +278,7 @@ Run:
5. Test that game starts without errors
Write results to .ai/verify_integration.md.
Write status.json: outcome=success if all pass, outcome=fail with failure_reason."
Write status.json: outcome=succeeded if all pass, outcome=failed with failure_reason."
]
check_integration [shape=diamond, label="Integration OK?"]
@ -314,39 +314,39 @@ Write detailed review to .ai/final_review.md including:
- Any issues found
- Compliance with spec
Write status.json: outcome=success if the game is complete and playable per spec, outcome=fail with what's missing or broken."
Write status.json: outcome=succeeded if the game is complete and playable per spec, outcome=failed with what's missing or broken."
]
check_review [shape=diamond, label="Review OK?"]
// Flow
start -> expand_spec -> impl_setup -> verify_setup -> check_setup
check_setup -> impl_data_structures [condition="outcome=success"]
check_setup -> impl_setup [condition="outcome=fail", label="retry"]
check_setup -> impl_data_structures [condition="outcome=succeeded"]
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
check_setup -> impl_setup [label="fallback"]
impl_data_structures -> verify_data_structures -> check_data_structures
check_data_structures -> impl_game_logic [condition="outcome=success"]
check_data_structures -> impl_data_structures [condition="outcome=fail", label="retry"]
check_data_structures -> impl_game_logic [condition="outcome=succeeded"]
check_data_structures -> impl_data_structures [condition="outcome=failed", label="retry"]
check_data_structures -> impl_data_structures [label="fallback"]
impl_game_logic -> verify_game_logic -> check_game_logic
check_game_logic -> impl_terminal_ui [condition="outcome=success"]
check_game_logic -> impl_game_logic [condition="outcome=fail", label="retry"]
check_game_logic -> impl_terminal_ui [condition="outcome=succeeded"]
check_game_logic -> impl_game_logic [condition="outcome=failed", label="retry"]
check_game_logic -> impl_game_logic [label="fallback"]
impl_terminal_ui -> verify_terminal_ui -> check_terminal_ui
check_terminal_ui -> impl_integration [condition="outcome=success"]
check_terminal_ui -> impl_terminal_ui [condition="outcome=fail", label="retry"]
check_terminal_ui -> impl_integration [condition="outcome=succeeded"]
check_terminal_ui -> impl_terminal_ui [condition="outcome=failed", label="retry"]
check_terminal_ui -> impl_terminal_ui [label="fallback"]
impl_integration -> verify_integration -> check_integration
check_integration -> review [condition="outcome=success"]
check_integration -> impl_integration [condition="outcome=fail", label="retry"]
check_integration -> review [condition="outcome=succeeded"]
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
check_integration -> impl_integration [label="fallback"]
review -> check_review
check_review -> exit [condition="outcome=success"]
check_review -> impl_terminal_ui [condition="outcome=fail", label="fix"]
check_review -> exit [condition="outcome=succeeded"]
check_review -> impl_terminal_ui [condition="outcome=failed", label="fix"]
check_review -> impl_terminal_ui [label="fallback"]
}

View file

@ -11,6 +11,6 @@ digraph Branch {
gate [shape=diamond, label="Tests passing?"]
start -> plan -> implement -> validate -> gate
gate -> exit [label="Yes", condition="outcome=success"]
gate -> exit [label="Yes", condition="outcome=succeeded"]
gate -> implement [label="No"]
}

View file

@ -9,7 +9,7 @@ digraph Conditions {
path_b [label="Path B"]
start -> decide
decide -> path_a [condition="outcome=success && context.mode=fast"]
decide -> path_a [condition="outcome=succeeded && context.mode=fast"]
decide -> path_b
path_a -> exit
path_b -> exit

View file

@ -521,9 +521,9 @@ Respond with exactly one of:\n\
// Bootstrap
start -> check_toolchain
check_toolchain -> expand_spec [condition="outcome=success"]
check_toolchain -> check_toolchain [condition="outcome=fail && context.failure_class=transient_infra", loop_restart=true]
check_toolchain -> postmortem [condition="outcome=fail && context.failure_class!=transient_infra"]
check_toolchain -> expand_spec [condition="outcome=succeeded"]
check_toolchain -> check_toolchain [condition="outcome=failed && context.failure_class=transient_infra", loop_restart=true]
check_toolchain -> postmortem [condition="outcome=failed && context.failure_class!=transient_infra"]
check_toolchain -> postmortem
expand_spec -> plan_fanout
@ -541,23 +541,23 @@ Respond with exactly one of:\n\
// Verify chain — failures go directly back to implement (errors logged to .workflow/verify_errors.log)
fix_fmt -> verify_fmt
verify_fmt -> gate_fmt
gate_fmt -> verify_build [condition="outcome=success"]
gate_fmt -> verify_build [condition="outcome=succeeded"]
gate_fmt -> implement
verify_build -> gate_build
gate_build -> verify_test [condition="outcome=success"]
gate_build -> verify_test [condition="outcome=succeeded"]
gate_build -> implement
verify_test -> gate_test
gate_test -> verify_browser [condition="outcome=success"]
gate_test -> verify_browser [condition="outcome=succeeded"]
gate_test -> implement
verify_browser -> gate_browser
gate_browser -> verify_artifacts [condition="outcome=success"]
gate_browser -> verify_artifacts [condition="outcome=succeeded"]
gate_browser -> implement
verify_artifacts -> gate_artifacts
gate_artifacts -> verify_fidelity [condition="outcome=success"]
gate_artifacts -> verify_fidelity [condition="outcome=succeeded"]
gate_artifacts -> implement
verify_fidelity -> gate_fidelity

View file

@ -581,7 +581,7 @@ Otherwise set preferred_next_label to \"more_work_needed\"."
review_codex -> fix_codex [label="Fallback"]
/* Phase 6: Build */
build_check -> final_audit_opus [label="Build OK", condition="outcome=success"]
build_check -> final_audit_opus [label="Build OK", condition="outcome=succeeded"]
build_check -> build_fix [label="Build failed"]
build_fix -> build_check

View file

@ -212,7 +212,7 @@ Otherwise set preferred_next_label to \"more_work_needed\"."
fix_batch -> build_check
/* Build check outcomes */
build_check -> final_audit [label="Build OK", condition="outcome=success"]
build_check -> final_audit [label="Build OK", condition="outcome=succeeded"]
build_check -> build_fix [label="Build failed"]
/* Build fix loops back to build check */

View file

@ -34,13 +34,13 @@ digraph NLSpecConformance {
// Wiring
start -> plan -> implement -> test_quick -> gate_quick
gate_quick -> test_full [label="Pass", condition="outcome=success"]
gate_quick -> test_full [label="Pass", condition="outcome=succeeded"]
gate_quick -> fix [label="Fix"]
fix -> test_quick
test_full -> gate_full
gate_full -> exit [label="Pass", condition="outcome=success"]
gate_full -> exit [label="Pass", condition="outcome=succeeded"]
gate_full -> fix [label="Fix"]
}

View file

@ -113,7 +113,7 @@ digraph SemanticPort {
plan -> implement -> validate -> gate
gate -> finalize [label="Pass", condition="outcome=success"]
gate -> finalize [label="Pass", condition="outcome=succeeded"]
gate -> fix [label="Fail"]
fix -> validate

View file

@ -41,7 +41,7 @@ digraph ImplementFeature {
implement -> test -> validate -> gate
gate -> review [label="Pass", condition="outcome=success"]
gate -> review [label="Pass", condition="outcome=succeeded"]
gate -> implement [label="Fix"]
review -> exit

View file

@ -11,6 +11,6 @@ digraph BranchLoop {
gate [shape=diamond, label="Tests passing?"]
start -> plan -> implement -> validate -> gate
gate -> exit [label="Pass", condition="outcome=success"]
gate -> exit [label="Pass", condition="outcome=succeeded"]
gate -> implement [label="Fix"]
}

View file

@ -10,6 +10,6 @@ digraph ImplementAndTest {
gate [shape=diamond, label="Tests passing?"]
start -> implement -> validate -> gate
gate -> exit [label="Pass", condition="outcome=success"]
gate -> exit [label="Pass", condition="outcome=succeeded"]
gate -> implement [label="Fix", max_visits=5]
}

View file

@ -18,7 +18,7 @@ digraph AllNodeTypes {
approve -> spec [label="[A] Approve"]
approve -> plan [label="[R] Revise"]
spec -> implement -> test -> gate
gate -> fork [label="Pass", condition="outcome=success"]
gate -> fork [label="Pass", condition="outcome=succeeded"]
gate -> cooldown [label="Fix"]
cooldown -> implement
fork -> security

View file

@ -21,7 +21,7 @@ digraph TransitionPatterns {
review -> implement [label="Fix"]
review -> gate [label="Approve"]
review -> gate [weight=-1]
gate -> fast_path [condition="outcome=success"]
gate -> fast_path [condition="outcome=succeeded"]
gate -> slow_path
fast_path -> preferred [weight=10]
fast_path -> exit [weight=1]