af-workflow 0.5.0

Spec-driven workflow chassis: typed node expressions composed into a branched DAG. Port of agent_core/workflow.
Documentation
//! Proves the Rust spec model reads a real builtin spec from the Python stack.

use af_workflow::Spec;

#[test]
fn deserializes_real_hello_world_builtin() {
    let raw = include_str!("fixtures/hello_world.json");
    let spec = Spec::from_json(raw).expect("hello_world.json should parse");

    assert_eq!(spec.spec_id, "hello_world");
    assert_eq!(spec.branches.len(), 1);

    let root = &spec.branches[0];
    assert_eq!(root.branch_id, "__root__");
    assert_eq!(root.nodes.len(), 3);
    assert_eq!(root.edges.len(), 2);

    // Node-type composition is preserved.
    let types: Vec<&str> = root.nodes.iter().map(|n| n.node_type.as_str()).collect();
    assert_eq!(
        types,
        ["ingress.cron", "transform.state_append", "sink.log"]
    );

    // Category/action split works on the real data.
    assert_eq!(root.nodes[1].category(), "transform");
    assert_eq!(root.nodes[1].action(), "state_append");

    // Config payload survives round-trip.
    assert_eq!(root.nodes[0].config["cron"], "*/5 * * * *");
    assert_eq!(root.nodes[1].config["max_len"], 1000);

    // Structural validation passes on a well-formed builtin.
    spec.validate_structure()
        .expect("structure should be valid");
}