Skip to content

Workflow Orchestration

Kohakku uses Temporal for durable workflow execution. Single-shot tasks do not need workflows -- use them when you need sequencing, fan-out, human gates, or durable execution.

Workflow Patterns

Pattern Description
Single Dispatch One task, wait for completion
Chained Sequential tasks, each gets prior result as context
Fan-Out N tasks in parallel, optional aggregation
Supervisor/Worker Supervisor dispatches sub-agents via signals
Review Loop Worker -> reviewer -> iterate until pass
Advisor Long-lived child workflow that wakes on signals

Pre-built Templates

Seed with manage.py seed_workflow_templates:

  • Coding Sprint -- plan -> fan-out impl -> review -> gate -> test -> merge
  • Coding Waterfall -- requirements -> design -> gate -> impl -> test -> gate -> deploy
  • Swarm Spike -- brief -> fan-out spike -> aggregate -> judge -> gate -> polish
  • Code Review Loop -- impl -> review -> decision gate -> test -> merge
  • Bug Fix -- investigate -> gate -> fix -> test -> review -> gate -> PR

Launching a Workflow

  1. Navigate to Workflows -> select template -> Launch
  2. Provide input context (JSON or text)
  3. Monitor progress on the instance detail page
  4. Approve/reject at human gates

Temporal Integration

Determinism Rules

Workflow code must be deterministic. No ORM calls, HTTP requests, or random numbers directly in workflow functions. All side effects go through activities.

# Wrong -- non-deterministic
task = Task.objects.get(guid=task_guid)

# Right -- side effect in an activity
result = await workflow.execute_activity(
    get_task_activity,
    task_guid,
    start_to_close_timeout=timedelta(seconds=30),
)

Activities

Activities bridge workflows to Django and the dispatch pipeline:

Activity Purpose
dispatch_task_activity Assembles brief, dispatches to Dispatcher
get_task_status_activity Reads task state from DB
update_workflow_status_activity Updates WorkflowInstance state
send_signal_activity Sends signals to other workflows

Activities use run_in_executor to bridge synchronous Django ORM operations into async Temporal activities.

Signals

Task callbacks trigger Temporal signals. Human gates wait for operator signals.

@workflow.signal
async def task_completed(self, result: dict):
    self.task_result = result
    self.task_done = True

Worker Process

The Temporal worker runs separately from Django:

python temporal_worker.py

Scale workers independently of Controller instances.

Human Gates

Insert a human gate between workflow stages:

  1. Workflow reaches a gate stage
  2. Workflow pauses (wait_condition)
  3. Operator reviews in the Controller UI
  4. Operator sends an approve/reject signal
  5. Workflow continues or aborts

Workflow Templates

WorkflowTemplate records define reusable patterns:

Field Description
category single, chained, fanout, supervisor, review, advisor
default_config JSON config applied to all instances
is_published Whether the template is available for use

Each template has ordered WorkflowStage records:

Field Description
order Execution order
stage_type dispatch, gate, aggregate, review
agent_definition Which agent to use
skills / configs M2M relations
on_failure fail, retry, skip

Reference Flows

See flows.md in the repo root for 13 reference flows demonstrating these patterns:

Pattern Flows
Single agentic loop 1, 3, 7
Fan-out / parallel 2
Sequential / chained 5, 6
Event-driven 3, 6, 7
Human gate 5
Human-in-the-loop (mid-task) 8
Dark factory (autonomous) 9
Supervisor / worker 10
Review / critique loop 11
Competitive dispatch 12
Agent-spawns-agent 13