Workflow (Agent DAG/State Machine): foundations
Status
accepted — 2026-05-09. Sector bootstrap (skeleton + 5 impl tickets) landed as part of /k-go services/ai audit wave (Modo C). Q1 resolved: YAML is the wire format (declarative graphs, lint-friendly, diff-friendly), Go SDK engines/sdk/go/workflow provides typed step authoring (PythonDart bindings follow as needed). Q2 resolved: separate sectors with explicit boundary — `servicesfoundationbpm` handles BPMN-style humanform-driven business process; services/ai/workflow handles agent-heavy execution; cross-sector calls allowed (subflow/subprocess).
Summary
DAGstate machine pra agents complexos — análogo LangGraphInngestTemporal AIRestate.
Motivation
agents/ é orquestração de agents singulares. Workflows multi-agent / multi-step com retry/state ainda não tem foundation. Bloqueia produção.
Scope
In
- DAG definition (YAML/code)
- State persistence
- Retry policies
- Branching
- Human-in-the-loop steps
Out (yet)
- Full BPMN (escopo
services/foundation/bpm) - Pipeline ETL
Initial design
Surfaces
backend/— Go API + state machine engineapp/web— UI minimal de runs (futuro)
Key APIs
POST /v1/workflows— registrarPOST /v1/workflows/{id}/runs— disparar runGET /v1/workflows/{id}/runs/{run_id}— fetch state
Dependencies
services/ai/agents— actorsservices/ai/trace— observabilityinfra/data/kdb-doc— stateservices/ai/sandbox— steps que rodam código
Relation to existing sectors
- Camada acima de agents
- Cooperativo com sandbox e trace
State mutation model (resolved 2026-05-25 — owner-ratified, workflow#010)
O guard scope é {state, steps, inputs} (engine evalScope, #008): guards/ expressões CEL leem inputs.* (inputs iniciais), steps.X.output (output de cada step concluído) e state.* (vars de estado). A escrita de state.* por um step usa output_to explícito (não merge implícito, não reducers):
- Campo opcional
output_to: <ident>no step. Ao concluir com sucesso, oengine faz
state[output_to] = outputantes de avaliar os guards das edges de saída (a própria edge do step pode rotear na var). Semoutput_to, o step não publica state var (comportamento N/N-1 inalterado — wire-format aditivo estável peralways-on.kmd). - Lint valida
output_tocomo identificador bare e rejeita os rootsreservados (
statestepsinputs) —WF-INVALID-OUTPUT-TO. - Reducers (
set: { var: "<expr CEL>" }— transformar o output antes degravar) ficam diferidos até ≥2 workflows reais precisarem (reuse-first/ hyperscale-first); migração é aditiva sobre
output_to.
Descartados: merge implícito do output no topo de state (clobber silencioso, sem controle do autor) e auto-store em state.<stepName> (duplica steps.X.output).
Self-hosted-first analysis (5 gates)
- G1 Feature parity: zero
- G2 Performance: N/A
- G3 Stability: N/A
- G4 Capability: Temporal-style viável
- G5 Critical-path readiness: destrava complex agents
Open questions
- Q1: DSL — YAML, código, ou ambos?
- Q2: Reuso de
services/foundation/bpmse existir?
Next steps
- Ratificar esta RFC (1 round de comments).
- Criar sector dir
services/ai/workflow/comkoder.toml,README.md, skeleton. - Abrir tickets de implementação em
services/ai/workflow/backlog/pending/. - Registrar em
meta/docs/stack/registries/self-hosted-pairs.mdse substituir externo.