moonflowgraph

A MoonBit task graph and provenance trace library for reproducible research and agent workflows.

workflow
dag
provenance
agent
research
moon add AlexenderSokolov/moonflowgraph@0.3.0
Download zip
Version
0.3.0
License
Apache-2.0
Last updated
last month
Downloads
41
README

#MoonFlowGraph Executable Examples

///|
test {
let graph = FlowGraph::new()
guard graph.add_task(TaskNode::new("collect_papers", "Collect papers"))
is Ok(_) else {
fail("expected first task")
}
guard graph.add_task(TaskNode::new("write_report", "Write report")) is Ok(_) else {
fail("expected second task")
}
guard graph.add_dependency(
TaskId::new("collect_papers"),
TaskId::new("write_report"),
)
is Ok(_) else {
fail("expected dependency")
}
guard graph.plan() is Ok(plan) else { fail("expected valid graph") }
debug_inspect(plan.order().length(), content="2")
debug_inspect(plan.batches().length(), content="2")
guard graph.roots() is Ok(roots) else { fail("expected roots") }
guard graph.leaves() is Ok(leaves) else { fail("expected leaves") }
debug_inspect(roots[0].value(), content="\"collect_papers\"")
debug_inspect(leaves[0].value(), content="\"write_report\"")
guard graph.to_mermaid() is Ok(mermaid) else { fail("expected Mermaid") }
debug_inspect(mermaid.contains("task_0 --> task_1"), content="true")
let trace = Trace::new()
guard graph.to_json_checked(plan, trace) is Ok(snapshot) else {
fail("expected checked JSON")
}
debug_inspect(snapshot.contains("\"schema_version\": 1"), content="true")
guard RunSnapshot::from_json(snapshot) is Ok(imported_snapshot) else {
fail("expected run snapshot import")
}
debug_inspect(imported_snapshot.audit() is Ok(_), content="true")
let workflow_json = graph.to_workflow_spec().to_json()
guard FlowGraph::from_workflow_json(workflow_json) is Ok(round_trip) else {
fail("expected workflow import")
}
debug_inspect(round_trip.task_count(), content="2")
}

#
Dependency

pub struct Dependency {
// private fields
} derive(Eq,
Debug
)

Directed dependency edge: before must finish before after.

#
Dependency::after

fn Dependency::after(self : Dependency) -> TaskId

Return the successor endpoint of this dependency.

#
Dependency::before

fn Dependency::before(self : Dependency) -> TaskId

Return the predecessor endpoint of this dependency.

#
Dependency::new

fn Dependency::new(before : TaskId, after : TaskId) -> Dependency

Build a dependency edge.

#
ExecutionPlan

pub struct ExecutionPlan {
// private fields
} derive(Eq,
Debug
)

A validated execution plan with serial order and parallel-ready batches.

#
ExecutionPlan::batches

fn ExecutionPlan::batches(self : ExecutionPlan) -> Array[Array[TaskId]]

Return a detached copy of the parallel execution batches.

#
ExecutionPlan::order

fn ExecutionPlan::order(self : ExecutionPlan) -> Array[TaskId]

Return a detached copy of the serial execution order.

#
ExecutionPlan::snapshot

fn ExecutionPlan::snapshot(self : ExecutionPlan) -> ExecutionPlan

Return a detached copy of an execution plan, including nested batches.

#
FlowGraph

pub struct FlowGraph {
// private fields
} derive(
Debug
)

Mutable workflow graph.

#
FlowGraph::add_dependency

fn FlowGraph::add_dependency(self : FlowGraph, before : TaskId, after : TaskId) -> Result[Unit, GraphError]

Add a dependency edge. Duplicate edges are rejected; endpoint existence is checked by validate.

#
FlowGraph::add_task

fn FlowGraph::add_task(self : FlowGraph, node : TaskNode) -> Result[Unit, GraphError]

Add a task node. Duplicate ids return DuplicateTask.

#
FlowGraph::dependencies

fn FlowGraph::dependencies(self : FlowGraph) -> Array[Dependency]

Return a detached copy of dependency edges.

#
FlowGraph::dependencies_snapshot

fn FlowGraph::dependencies_snapshot(self : FlowGraph) -> Array[Dependency]

Return a detached copy of dependency edges.

#
FlowGraph::dependency_count

fn FlowGraph::dependency_count(self : FlowGraph) -> Int

Return the number of dependency edges.

#
FlowGraph::execution_batches

fn FlowGraph::execution_batches(self : FlowGraph) -> Result[Array[Array[TaskId]], GraphError]

Return parallel-ready batches. Tasks in the same batch have no dependencies between them.

#
FlowGraph::find_task

fn FlowGraph::find_task(self : FlowGraph, id : TaskId) -> TaskNode?

Find a task by id.

#
FlowGraph::from_workflow_json

fn FlowGraph::from_workflow_json(input : String) -> Result[FlowGraph, WorkflowSpecError]

Parse workflow-spec-v1 JSON and return a validated graph.

#
FlowGraph::leaves

fn FlowGraph::leaves(self : FlowGraph) -> Result[Array[TaskId], GraphError]

Return tasks with no outgoing dependency edges.

#
FlowGraph::new

fn FlowGraph::new() -> FlowGraph

Create an empty flow graph.

#
FlowGraph::plan

fn FlowGraph::plan(self : FlowGraph) -> Result[ExecutionPlan, GraphError]

Create a full execution plan.

#
FlowGraph::predecessors

fn FlowGraph::predecessors(self : FlowGraph, id : TaskId) -> Result[Array[TaskId], GraphError]

Return immediate predecessor ids for a task.

#
FlowGraph::ready_tasks

fn FlowGraph::ready_tasks(self : FlowGraph, done : Array[TaskId]) -> Result[Array[TaskId], GraphError]

Return tasks whose predecessors are all in done.

#
FlowGraph::roots

fn FlowGraph::roots(self : FlowGraph) -> Result[Array[TaskId], GraphError]

Return tasks with no incoming dependency edges.

#
FlowGraph::runnable_tasks

fn FlowGraph::runnable_tasks(self : FlowGraph) -> Result[Array[TaskId], GraphError]

Return pending or ready tasks whose predecessors have succeeded.

Failed or skipped predecessors block their successors. Callers that manage completion separately should continue using ready_tasks(done).

#
FlowGraph::snapshot

fn FlowGraph::snapshot(self : FlowGraph) -> FlowGraph

Return a detached copy of this graph and all task metadata arrays.

#
FlowGraph::snapshot_json

fn FlowGraph::snapshot_json(self : FlowGraph, trace : Trace) -> Result[String, SnapshotError]

Build a fresh plan, validate trace references, and render JSON.

#
FlowGraph::snapshot_markdown

fn FlowGraph::snapshot_markdown(self : FlowGraph, trace : Trace) -> Result[String, SnapshotError]

Build a fresh plan, validate trace references, and render Markdown.

#
FlowGraph::successors

fn FlowGraph::successors(self : FlowGraph, id : TaskId) -> Result[Array[TaskId], GraphError]

Return immediate successor ids for a task.

#
FlowGraph::task_count

fn FlowGraph::task_count(self : FlowGraph) -> Int

Return the number of task nodes.

#
FlowGraph::tasks

fn FlowGraph::tasks(self : FlowGraph) -> Array[TaskNode]

Return a detached copy of task nodes and their metadata arrays.

#
FlowGraph::tasks_snapshot

fn FlowGraph::tasks_snapshot(self : FlowGraph) -> Array[TaskNode]

Return a detached copy of task nodes and their metadata arrays.

#
FlowGraph::to_json

fn FlowGraph::to_json(self : FlowGraph, plan : ExecutionPlan, trace : Trace) -> String

Render a compact JSON string for tooling or snapshot artifacts.

This compatibility API trusts the supplied plan. New code should prefer to_json_checked or snapshot_json.

#
FlowGraph::to_json_checked

fn FlowGraph::to_json_checked(self : FlowGraph, plan : ExecutionPlan, trace : Trace) -> Result[String, SnapshotError]

Validate a supplied plan and trace before rendering JSON.

#
FlowGraph::to_markdown

fn FlowGraph::to_markdown(self : FlowGraph, plan : ExecutionPlan, trace : Trace) -> String

Render a graph, plan, and trace as a Markdown execution report.

This compatibility API trusts the supplied plan. New code should prefer to_markdown_checked or snapshot_markdown.

#
FlowGraph::to_markdown_checked

fn FlowGraph::to_markdown_checked(self : FlowGraph, plan : ExecutionPlan, trace : Trace) -> Result[String, SnapshotError]

Validate a supplied plan and trace before rendering Markdown.

#
FlowGraph::to_mermaid

fn FlowGraph::to_mermaid(self : FlowGraph) -> Result[String, GraphError]

Render a validated task graph as a Mermaid flowchart.

#
FlowGraph::to_workflow_spec

fn FlowGraph::to_workflow_spec(self : FlowGraph) -> WorkflowSpec

Convert a graph into workflow-spec-v1 input JSON semantics.

Runtime task status is intentionally not preserved in workflow specs.

#
FlowGraph::topological_sort

fn FlowGraph::topological_sort(self : FlowGraph) -> Result[Array[TaskId], GraphError]

Return a topological execution order.

#
FlowGraph::transition_status

fn FlowGraph::transition_status(self : FlowGraph, id : TaskId, status : TaskStatus) -> Result[Unit, StatusTransitionError]

Update a task through the documented execution-state machine.

update_status remains available for replay and compatibility. Normal execution should prefer this checked method.

#
FlowGraph::update_status

fn FlowGraph::update_status(self : FlowGraph, id : TaskId, status : TaskStatus) -> Result[Unit, GraphError]

Update the status of an existing task.

#
FlowGraph::validate

fn FlowGraph::validate(self : FlowGraph) -> Result[Unit, GraphError]

Validate all edge endpoints and cycle freedom.

#
FlowGraph::validate_plan

fn FlowGraph::validate_plan(self : FlowGraph, plan : ExecutionPlan) -> Result[Unit, SnapshotError]

Check that a plan still exactly matches the graph's deterministic plan.

#
FlowGraph::validate_snapshot

fn FlowGraph::validate_snapshot(self : FlowGraph, plan : ExecutionPlan, trace : Trace) -> Result[Unit, SnapshotError]

Check graph validity, plan freshness, and trace task references together.

#
FlowGraph::validate_trace

fn FlowGraph::validate_trace(self : FlowGraph, trace : Trace) -> Result[Unit, SnapshotError]

Check that every trace event refers to a task in this graph.

#
GraphError

pub(all) enum GraphError {
DuplicateTask(TaskId)
DuplicateDependency(Dependency)
MissingTask(TaskId)
MissingDependencyEndpoint(Dependency)
CycleDetected(Array[TaskId])
} derive(Eq,
Debug
)

Graph errors retain the ids and edges needed for useful diagnostics.

#
GraphError::message

fn GraphError::message(self : GraphError) -> String

Return a readable diagnostic message for a graph error.

#
RunSnapshot

pub struct RunSnapshot {
// private fields
} derive(
Debug
)

A validated run-snapshot-v1 artifact containing graph, plan, and trace data.

#
RunSnapshot::audit

fn RunSnapshot::audit(self : RunSnapshot) -> Result[Unit, RunSnapshotError]

Validate graph structure, serialized plan, trace references, and lifecycle.

#
RunSnapshot::from_json

fn RunSnapshot::from_json(input : String) -> Result[RunSnapshot, RunSnapshotError]

Parse and audit a run-snapshot-v1 JSON string.

#
RunSnapshot::graph

fn RunSnapshot::graph(self : RunSnapshot) -> FlowGraph

Return a detached graph copy.

#
RunSnapshot::plan

Return a detached execution-plan copy.

#
RunSnapshot::to_json

fn RunSnapshot::to_json(self : RunSnapshot) -> String

Render this audited snapshot in canonical run-snapshot-v1 form.

#
RunSnapshot::trace

fn RunSnapshot::trace(self : RunSnapshot) -> Trace

Return a detached trace copy.

#
RunSnapshotError

pub(all) enum RunSnapshotError {
InvalidRunSnapshotJson(String)
UnsupportedRunSnapshotSchema(String)
MissingRunSnapshotField(String)
InvalidRunSnapshotField(String)
RunSnapshotGraphError(GraphError)
RunSnapshotPlanMismatch
RunSnapshotUnknownTraceTask(TaskId)
InvalidTraceLifecycle(TaskId, TraceEventType)
RunSnapshotStatusMismatch(TaskId, TaskStatus, String)
} derive(Eq,
Debug
)

Errors raised while parsing or auditing run-snapshot-v1 JSON.

#
RunSnapshotError::message

fn RunSnapshotError::message(self : RunSnapshotError) -> String

Return a readable diagnostic for snapshot import and audit errors.

#
SnapshotError

pub(all) enum SnapshotError {
SnapshotGraphError(GraphError)
StaleExecutionPlan
UnknownTraceTask(TaskId)
} derive(Eq,
Debug
)

Errors raised when a plan or trace no longer matches its graph.

#
SnapshotError::message

fn SnapshotError::message(self : SnapshotError) -> String

Return a readable diagnostic for snapshot validation.

#
StatusTransitionError

pub(all) enum StatusTransitionError {
TransitionMissingTask(TaskId)
InvalidStatusTransition(TaskId, TaskStatus, TaskStatus)
} derive(Eq,
Debug
)

Errors raised by checked task-status transitions.

#
StatusTransitionError::message

fn StatusTransitionError::message(self : StatusTransitionError) -> String

Return a readable diagnostic for a checked status transition.

#
TaskId

pub struct TaskId {
// private fields
} derive(Eq,
Debug
)

Task identifier used by the public graph API.

#
TaskId::new

fn TaskId::new(value : String) -> TaskId

Build a task id.

#
TaskId::value

fn TaskId::value(self : TaskId) -> String

Return the raw string value of this task id.

#
TaskNode

pub struct TaskNode {
// private fields
} derive(Eq,
Debug
)

A node in a reproducible research or agent workflow.

#
TaskNode::description

fn TaskNode::description(self : TaskNode) -> String

Return this task's description.

#
TaskNode::id

fn TaskNode::id(self : TaskNode) -> TaskId

Return this task's id.

#
TaskNode::inputs

fn TaskNode::inputs(self : TaskNode) -> Array[String]

Return a detached copy of this task's input labels.

#
TaskNode::new

fn TaskNode::new(id : String, title : String) -> TaskNode

Build a basic pending task.

#
TaskNode::outputs

fn TaskNode::outputs(self : TaskNode) -> Array[String]

Return a detached copy of this task's output labels.

#
TaskNode::snapshot

fn TaskNode::snapshot(self : TaskNode) -> TaskNode

Return a detached copy of a task node and its metadata arrays.

#
TaskNode::status

fn TaskNode::status(self : TaskNode) -> TaskStatus

Return this task's execution status.

#
TaskNode::tags

fn TaskNode::tags(self : TaskNode) -> Array[String]

Return a detached copy of this task's tags.

#
TaskNode::title

fn TaskNode::title(self : TaskNode) -> String

Return this task's title.

#
TaskNode::with_description

fn TaskNode::with_description(self : TaskNode, description : String) -> TaskNode

Add a short description.

#
TaskNode::with_inputs

fn TaskNode::with_inputs(self : TaskNode, inputs : Array[String]) -> TaskNode

Add input labels or artifacts.

#
TaskNode::with_outputs

fn TaskNode::with_outputs(self : TaskNode, outputs : Array[String]) -> TaskNode

Add output labels or artifacts.

#
TaskNode::with_status

fn TaskNode::with_status(self : TaskNode, status : TaskStatus) -> TaskNode

Override status.

#
TaskNode::with_tags

fn TaskNode::with_tags(self : TaskNode, tags : Array[String]) -> TaskNode

Add topic tags.

#
TaskStatus

pub(all) enum TaskStatus {
Pending
Ready
Running
Succeeded
Failed(String)
Skipped(String)
} derive(Eq,
Debug
)

Execution status tracked for each task node.

#
TaskStatus::can_transition_to

fn TaskStatus::can_transition_to(self : TaskStatus, next : TaskStatus) -> Bool

Return whether a status transition is valid for normal execution.

#
TaskStatus::kind

fn TaskStatus::kind(self : TaskStatus) -> String

Return a stable machine-readable status kind without an error reason.

#
TaskStatus::label

fn TaskStatus::label(self : TaskStatus) -> String

Return a stable human-readable task status label.

#
TaskStatus::reason

fn TaskStatus::reason(self : TaskStatus) -> String?

Return the reason attached to a failed or skipped status.

#
Trace

pub struct Trace {
// private fields
} derive(
Debug
)

Mutable trace log.

#
Trace::event_count

fn Trace::event_count(self : Trace) -> Int

Return the number of recorded events.

#
Trace::events

fn Trace::events(self : Trace) -> Array[TraceEvent]

Return a detached copy of events in append order.

#
Trace::events_for

fn Trace::events_for(self : Trace, task_id : TaskId) -> Array[TraceEvent]

Return events for a single task id in append order.

#
Trace::events_snapshot

fn Trace::events_snapshot(self : Trace) -> Array[TraceEvent]

Return a detached copy of events in append order.

#
Trace::latest_for

fn Trace::latest_for(self : Trace, task_id : TaskId) -> TraceEvent?

Return the most recently appended event for a task.

#
Trace::new

fn Trace::new() -> Trace

Build an empty trace log.

#
Trace::record

fn Trace::record(self : Trace, event : TraceEvent) -> Unit

Append an event.

#
Trace::snapshot

fn Trace::snapshot(self : Trace) -> Trace

Return a detached copy of this trace log.

#
Trace::summary_markdown

fn Trace::summary_markdown(self : Trace) -> String

Render a compact Markdown summary of trace events.

#
TraceEvent

pub struct TraceEvent {
// private fields
} derive(Eq,
Debug
)

A single trace event. Timestamp is a string so users can choose wall-clock, logical, or deterministic timestamps.

#
TraceEvent::event_type

fn TraceEvent::event_type(self : TraceEvent) -> TraceEventType

Return this event's type.

#
TraceEvent::message

fn TraceEvent::message(self : TraceEvent) -> String

Return this event's human-readable message.

#
TraceEvent::new

fn TraceEvent::new(task_id : TaskId, event_type : TraceEventType, message : String, timestamp : String) -> TraceEvent

Build a trace event.

#
TraceEvent::task_id

fn TraceEvent::task_id(self : TraceEvent) -> TaskId

Return the task id associated with this event.

#
TraceEvent::timestamp

fn TraceEvent::timestamp(self : TraceEvent) -> String

Return this event's timestamp string.

#
TraceEventType

pub(all) enum TraceEventType {
Planned
Started
Completed
FailedEvent
SkippedEvent
Note
} derive(Eq,
Debug
)

Event type for provenance trace.

#
TraceEventType::label

fn TraceEventType::label(self : TraceEventType) -> String

Return a stable human-readable event type label.

#
WorkflowSpec

pub struct WorkflowSpec {
// private fields
} derive(Eq,
Debug
)

Workflow input definition, separated from run-snapshot JSON output.

#
WorkflowSpec::dependencies

fn WorkflowSpec::dependencies(self : WorkflowSpec) -> Array[Dependency]

Return a detached copy of workflow dependency edges.

#
WorkflowSpec::from_json

fn WorkflowSpec::from_json(input : String) -> Result[WorkflowSpec, WorkflowSpecError]

Parse, type-check, and validate a workflow-spec-v1 JSON string.

#
WorkflowSpec::new

fn WorkflowSpec::new(tasks : Array[WorkflowTaskSpec], dependencies : Array[Dependency]) -> WorkflowSpec

Build a workflow spec from tasks and dependency edges.

#
WorkflowSpec::tasks

Return a detached copy of workflow task specs.

#
WorkflowSpec::to_graph

fn WorkflowSpec::to_graph(self : WorkflowSpec) -> Result[FlowGraph, GraphError]

Convert this workflow definition into a validated flow graph.

#
WorkflowSpec::to_json

fn WorkflowSpec::to_json(self : WorkflowSpec) -> String

Render this workflow spec as workflow-spec-v1 JSON.

#
WorkflowSpecError

pub(all) enum WorkflowSpecError {
InvalidWorkflowJson(String)
UnsupportedWorkflowSchema(String)
MissingWorkflowField(String)
InvalidWorkflowField(String)
WorkflowGraphError(GraphError)
} derive(Eq,
Debug
)

Errors raised while parsing and validating workflow-spec-v1 JSON.

#
WorkflowSpecError::message

fn WorkflowSpecError::message(self : WorkflowSpecError) -> String

Return a readable diagnostic for workflow spec errors.

#
WorkflowTaskSpec

pub struct WorkflowTaskSpec {
// private fields
} derive(Eq,
Debug
)

A task definition in workflow-spec-v1 input JSON.

Unlike TaskNode, this type models user-supplied workflow structure and does not carry runtime execution status.

#
WorkflowTaskSpec::description

fn WorkflowTaskSpec::description(self : WorkflowTaskSpec) -> String

Return this workflow task description.

#
WorkflowTaskSpec::id

Return this workflow task id.

#
WorkflowTaskSpec::inputs

fn WorkflowTaskSpec::inputs(self : WorkflowTaskSpec) -> Array[String]

Return a detached copy of this workflow task's input labels.

#
WorkflowTaskSpec::new

fn WorkflowTaskSpec::new(id : String, title : String) -> WorkflowTaskSpec

Build a workflow task spec.

#
WorkflowTaskSpec::outputs

fn WorkflowTaskSpec::outputs(self : WorkflowTaskSpec) -> Array[String]

Return a detached copy of this workflow task's output labels.

#
WorkflowTaskSpec::snapshot

Return a detached copy of a workflow task spec.

#
WorkflowTaskSpec::tags

fn WorkflowTaskSpec::tags(self : WorkflowTaskSpec) -> Array[String]

Return a detached copy of this workflow task's tags.

#
WorkflowTaskSpec::title

fn WorkflowTaskSpec::title(self : WorkflowTaskSpec) -> String

Return this workflow task title.

#
WorkflowTaskSpec::with_description

fn WorkflowTaskSpec::with_description(self : WorkflowTaskSpec, description : String) -> WorkflowTaskSpec

Add a short description to a workflow task spec.

#
WorkflowTaskSpec::with_inputs

fn WorkflowTaskSpec::with_inputs(self : WorkflowTaskSpec, inputs : Array[String]) -> WorkflowTaskSpec

Add input labels or artifacts to a workflow task spec.

#
WorkflowTaskSpec::with_outputs

fn WorkflowTaskSpec::with_outputs(self : WorkflowTaskSpec, outputs : Array[String]) -> WorkflowTaskSpec

Add output labels or artifacts to a workflow task spec.

#
WorkflowTaskSpec::with_tags

fn WorkflowTaskSpec::with_tags(self : WorkflowTaskSpec, tags : Array[String]) -> WorkflowTaskSpec

Add topic tags to a workflow task spec.