Pipeline Graph
This is the same graph view boboddy pipelines studio renders locally over a live pipeline, embedded here over static, hand-authored fixtures instead of a running server. Each fixture below is authored with definePipeline() — the same builder used to write a real pipeline — which compiles down to a PipelineDefinitionSpec. That spec is translated into {nodes, edges} at build time (translateSpecToGraph, @boboddy/pipeline-studio-ui’s "." export), then rendered client-side by the same PipelineGraphView component the studio uses (its "./view" export). Drag to pan, scroll to zoom, and use the controls in the bottom-left corner to fit or reset the view.
These examples cover node kinds worth seeing on a graph — a plain linear chain and a choice branch so far, with fanOut, parallel, and loop examples still to come.
Linear chain
Section titled “Linear chain”The simplest shape: a sequence of steps with no branching, ending in a terminal succeed node.
Triage a bug report
Reads the report, analyzes its severity, and writes a summary.
View fixture source
import { z } from "zod";
import { defineStep } from "@boboddy/sdk/definitions/steps";
import { definePipeline } from "@boboddy/sdk/definitions/pipelines";
/**
* Linear chain — three sequential steps then a terminal `succeed`. The
* simplest shape a pipeline graph can take: no branching, no fan-out, no
* loop. Authored with `definePipeline()` — the same builder real pipelines
* are written with — rather than the compiled `PipelineDefinitionSpec` wire shape it
* produces. The steps below are illustrative stand-ins, never pushed or
* executed (see `docs/research/pipeline-graph-docs-catalog.md` §7).
*/
const readReportStep = defineStep({
key: "intake-step",
name: "Read report",
agentPrompt: "Read the incoming bug report and extract its key details.",
result: z.object({ reportText: z.string() }),
});
const analyzeSeverityStep = defineStep({
key: "analyze-step",
name: "Analyze severity",
agentPrompt: "Analyze how severe the bug report is.",
result: z.object({ severity: z.string() }),
});
const writeSummaryStep = defineStep({
key: "summarize-step",
name: "Write summary",
agentPrompt: "Write a short summary of the triage outcome.",
result: z.object({ summary: z.string() }),
});
export const linearChainSpec = definePipeline({
key: "catalog-linear-chain",
name: "Triage a bug report",
description: "Reads the report, analyzes its severity, and writes a summary.",
startAt: "intake",
states: {
intake: { kind: "step", step: readReportStep, next: "analyze" },
analyze: { kind: "step", step: analyzeSeverityStep, next: "summarize" },
summarize: { kind: "step", step: writeSummaryStep, next: "done" },
done: { kind: "succeed" },
},
});Choice
Section titled “Choice”A choice node routes to one of several targets based on its choices[] conditions, falling back to default when none match.
Route an incident by severity
Classifies an incident, then pages on-call or files a ticket.
View fixture source
import { z } from "zod";
import { defineStep } from "@boboddy/sdk/definitions/steps";
import { definePipeline, Rule } from "@boboddy/sdk/definitions/pipelines";
/**
* Choice — one step feeds a routing node with a matched case (`critical`)
* and a `default` fallback, each continuing to its own terminal `succeed`.
* Authored with `definePipeline()` — the same builder real pipelines are
* written with — rather than the compiled `PipelineDefinitionSpec` wire
* shape it produces (see `linear-chain.ts`'s note). A `choice` state's
* `choices[].when` is the routing source of truth; `definePipeline()`
* derives the graph's edges from it.
*/
const classifySeverityStep = defineStep({
key: "classify-step",
name: "Classify severity",
agentPrompt: "Classify the incident's severity.",
result: z.object({ severity: z.string() }),
signals: [{ sourcePath: "severity", key: "severity" }],
});
const pageOncallStep = defineStep({
key: "page-oncall-step",
name: "Page on-call",
agentPrompt: "Page the on-call engineer about this incident.",
result: z.object({ paged: z.boolean() }),
});
const fileTicketStep = defineStep({
key: "file-ticket-step",
name: "File a ticket",
agentPrompt: "File a tracking ticket for this incident.",
result: z.object({ ticketId: z.string() }),
});
export const choiceSpec = definePipeline({
key: "catalog-choice",
name: "Route an incident by severity",
description: "Classifies an incident, then pages on-call or files a ticket.",
startAt: "classify",
states: {
classify: { kind: "step", step: classifySeverityStep, next: "routeBySeverity" },
routeBySeverity: {
kind: "choice",
choices: [
{ when: Rule.when("severity", "equal", "critical"), next: "pageOncall" },
],
default: "fileTicket",
},
pageOncall: { kind: "step", step: pageOncallStep, next: "paged" },
fileTicket: { kind: "step", step: fileTicketStep, next: "ticketed" },
paged: { kind: "succeed" },
ticketed: { kind: "succeed" },
},
});