af-workflow 0.5.0

Spec-driven workflow chassis: typed node expressions composed into a branched DAG. Port of agent_core/workflow.
Documentation
{
  "spec_id": "hello_world",
  "version": "1.0",
  "description": "Canary workflow — fires on cron, appends fired_at timestamp to a bounded list, then emits structlog heartbeat. PR3 cutover replaces the legacy @workflow hello_world (which used ctx.state.set('count', n+1)) with this spec; observability is richer (timestamp ring vs scalar counter) and the spec exercises the v10 ingress.cron + transform.state_append + sink.log path end-to-end.",
  "display": {
    "trigger": {
      "title_key": "wf.hello_world.trigger.title",
      "subtitle_key": "wf.shared.cron_subtitle",
      "icon": "clock"
    },
    "record_tick": {
      "title_key": "wf.hello_world.state.title",
      "subtitle_key": "wf.hello_world.state.subtitle",
      "icon": "database"
    },
    "log": {
      "title_key": "wf.hello_world.log.title",
      "subtitle_key": "wf.hello_world.log.subtitle",
      "icon": "activity"
    }
  },
  "branches": [
    {
      "branch_id": "__root__",
      "nodes": [
        {
          "id": "trigger",
          "type": "ingress.cron",
          "config": {"cron": "*/5 * * * *"}
        },
        {
          "id": "record_tick",
          "type": "transform.state_append",
          "config": {"path": "fired_at", "key": "ticks", "max_len": 1000}
        },
        {
          "id": "log",
          "type": "sink.log",
          "config": {"level": "info", "message": "hello_world_tick"}
        }
      ],
      "edges": [
        {"source": "trigger", "target": "record_tick"},
        {"source": "record_tick", "target": "log"}
      ]
    }
  ]
}