feat: workflow graph system — blocks 1-20 complete checkpoint

Full graph-based workflow execution engine with:
- WorkflowGraphRuntime with two-phase dispatch (collect → fire Celery tasks)
- Node registry with contract validation, socket types, execution kinds
- WorkflowRuntimeServices: preflight, context resolution, shadow-mode A/B
- WorkflowRouter: CRUD + dispatch/preflight/run-history API endpoints
- OutputTypeContracts: workflow binding, rollout-mode resolution
- Admin router: output-type workflow binding endpoints
- Frontend: complete workflow editor with drag-drop canvas, node inspector,
  module bundles, reference bundles, preflight panel, validation banner,
  authoring guidance, blueprint templates, shadow/rollout gate UI
- Tests: comprehensive coverage for all workflow modules
- Docs: NODE_CONTRACT_AUDIT and VALIDATION_ERROR_INVENTORY

All 20 blocks from NEXT_20_BLOCK_BATCH_PLAN completed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 19:09:54 +02:00
co-authored by Claude Sonnet 4.6
parent c51dd8cd67
commit d2e4934cca
63 changed files with 7035 additions and 2140 deletions
@@ -19,7 +19,13 @@ _PRESET_TYPES = {
}
_EXECUTION_MODES = {"legacy", "graph", "shadow"}
_WORKFLOW_BLUEPRINTS = {"cad_intake", "order_rendering", "still_graph_reference"}
_WORKFLOW_BLUEPRINTS = {
"cad_intake",
"order_rendering",
"still_graph_reference",
"still_graph_alpha_reference",
"still_graph_blend_reference",
}
_WORKFLOW_STARTERS = {"cad_file", "order_line"}
_WORKFLOW_STARTER_BLUEPRINTS = {
"starter_cad_intake": "cad_file",
@@ -74,9 +80,16 @@ def _extract_render_params_from_nodes(nodes: list[dict[str, Any]], step: StepNam
return {}
def _build_order_line_still_graph_nodes(render_params: dict[str, Any]) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
def _build_order_line_still_graph_nodes(
render_params: dict[str, Any],
*,
transparent_bg: bool | None = None,
include_blend_export: bool = False,
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
graph_render_params = deepcopy(render_params)
graph_render_params.setdefault("use_custom_render_settings", False)
if transparent_bg is not None:
graph_render_params["transparent_bg"] = transparent_bg
nodes = [
_make_node("setup", StepName.ORDER_LINE_SETUP, 0, 160, label="Order Line Setup"),
@@ -108,6 +121,29 @@ def _build_order_line_still_graph_nodes(render_params: dict[str, Any]) -> tuple[
{"from": "render", "to": "output"},
{"from": "render", "to": "notify"},
]
if include_blend_export:
nodes.extend(
[
_make_node("blend_export", StepName.EXPORT_BLEND, 920, 360, label="Export Blend"),
_make_node(
"save_blend",
StepName.OUTPUT_SAVE,
1160,
320,
params={"expected_artifact_role": "blend_export"},
label="Save Blend Output",
),
_make_node("notify_blend", StepName.NOTIFY, 1160, 420, label="Notify Blend Export"),
]
)
edges.extend(
[
{"from": "setup", "to": "blend_export"},
{"from": "template", "to": "blend_export"},
{"from": "blend_export", "to": "save_blend"},
{"from": "blend_export", "to": "notify_blend"},
]
)
return nodes, edges
@@ -329,6 +365,16 @@ def build_workflow_blueprint_config(blueprint: str) -> dict[str, Any]:
nodes, edges = _build_order_line_still_graph_nodes(
{"render_engine": "cycles", "samples": 256, "width": 1920, "height": 1080}
)
elif blueprint == "still_graph_alpha_reference":
nodes, edges = _build_order_line_still_graph_nodes(
{"render_engine": "cycles", "samples": 256, "width": 1920, "height": 1080},
transparent_bg=True,
)
elif blueprint == "still_graph_blend_reference":
nodes, edges = _build_order_line_still_graph_nodes(
{"render_engine": "cycles", "samples": 256, "width": 1920, "height": 1080},
include_blend_export=True,
)
return {
"version": 1,
@@ -336,7 +382,7 @@ def build_workflow_blueprint_config(blueprint: str) -> dict[str, Any]:
"edges": edges,
"ui": {
"preset": "custom",
"execution_mode": "graph" if blueprint == "still_graph_reference" else "legacy",
"execution_mode": "graph" if blueprint.startswith("still_graph_") else "legacy",
"family": "cad_file" if blueprint == "cad_intake" else "order_line",
"blueprint": blueprint,
},