Domain Controllers
The business intelligence layer of the Weblisk architecture. Domain controllers sit between the orchestrator and work agents — receiving business-level tasks, decomposing them into discrete work units, dispatching those units to specialised agents, aggregating results, and feeding outcomes back into the continuous optimisation lifecycle.
Nothing enters or leaves a business function without the domain controller's knowledge and approval. It is the director: it understands the process, the rules, the data contracts, and the quality gates that make each business function operate correctly.
Architecture
┌──────────────────────────────────────────────────────┐
│ Orchestrator │
│ Registration · Routing · Security · Discovery │
└────────────┬────────────────────┬────────────────────┘
│ task │ task
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ SEO Domain │ │ Ops Domain │
│ (controller) │ │ (controller) │
└──┬──────┬───────┘ └──┬──────┬───────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────┐┌─────┐ ┌─────┐┌─────┐
│ seo ││a11y │ │ cron││email│
│ anlz││chkr │ │ agnt││agnt │
└─────┘└─────┘ └─────┘└─────┘
work work infra infra
agents agents agents agents
| Component | Role |
|---|---|
| Orchestrator | Coordinates between domains. Manages registration, discovery, security, and audit for the entire system. |
| Domain controller | Directs a business function. Owns one or more workflows, dispatches to agents, aggregates results, enforces quality gates. A domain IS an agent (implements the same 5 protocol endpoints) but with additional responsibilities. |
| Work agents | Perform specific tasks dispatched by a domain. They have no independent initiative — they receive input, execute, return output. |
| Infrastructure agents | System-level utilities (sync, cron, webhook, email). Any domain or the orchestrator can use them. They are not owned by a specific domain. |
Domain Manifest
A domain controller registers with the orchestrator using the standard POST /v1/register flow.
Its manifest uses the same AgentManifest structure with domain-specific fields:
{ "name": "seo", "type": "domain", "version": "1.0.0", "description": "SEO optimization — audits, recommendations, and automated fixes", "url": "http://localhost:9700", "public_key": "<hex Ed25519 public key>", "capabilities": [ {"name": "agent:message", "resources": []}, {"name": "workflow:execute", "resources": []} ], "inputs": [ {"name": "target_files", "type": "file_list", "description": "Files to optimize"} ], "outputs": [ {"name": "domain_report", "type": "json", "description": "Aggregated results with recommendations"} ], "approval": "required", "required_agents": ["seo-analyzer", "a11y-checker"], "workflows": ["seo-audit", "seo-optimize"] }
Domain-Specific Fields
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | MUST be "domain" (vs "agent" or "infrastructure") |
required_agents | []string | Yes | Agent names this domain dispatches to |
workflows | []string | Yes | Workflow names this domain supports |
The type field tells the orchestrator how to route tasks and track dependencies.
Registration & Dependency Verification
Domain registration follows the standard protocol with additional orchestrator behaviour:
- Domain sends
RegisterRequest(same as any agent) - Orchestrator verifies signature and identity (standard flow)
- Orchestrator reads
manifest.type = "domain" - Orchestrator reads
manifest.required_agents - For each required agent: check if present in the registry
- If ALL present → domain status =
"online" - If ANY missing → domain status =
"degraded"(log warning with missing agents)
- If ALL present → domain status =
- Issue token and respond (standard flow)
- Broadcast service directory (standard flow)
Late agent registration: when a missing agent registers later, the orchestrator checks if any
domain requires it. If the domain was degraded and all required agents are now present, the domain is promoted
to "online". This allows agents and domains to start in any order.
Workflow Specification
Every domain defines one or more workflows — multi-step, multi-agent processes that accomplish a business function.
Workflows are defined in the domain's blueprint file (in domains/), not in the runtime manifest
(which only lists workflow names).
workflow: seo-audit description: Full SEO audit — scan, analyze, check accessibility, report trigger: action = "audit" phases: - name: scan agent: seo-analyzer action: scan_html input: html_files: $task.payload.files output: scan_results timeout: 60 - name: analyze agent: seo-analyzer action: analyze_metadata input: metadata: $phases.scan.output.metadata output: analysis_results timeout: 120 depends_on: [scan] - name: accessibility agent: a11y-checker action: check_images input: images: $phases.scan.output.images output: a11y_results timeout: 60 depends_on: [scan] - name: synthesize agent: self action: aggregate_results input: analysis: $phases.analyze.output a11y: $phases.accessibility.output output: final_report depends_on: [analyze, accessibility] approval: required
Phase Fields
| Field | Required | Description |
|---|---|---|
name | Yes | Unique identifier within this workflow |
agent | Yes | Target agent name, or "self" for domain-internal logic |
action | Yes | HandleMessage action to invoke on target |
input | Yes | Reference expressions binding data from task, phases, entity, or config |
output | Yes | Key name for phase result — used in $phases.<output> references |
depends_on | No | Phases that must complete first; drives execution order |
timeout | No | Phase timeout in seconds (default: 300) |
approval | No | "required" or "auto" (default: "auto") |
on_error | No | "fail" (default), "skip", or "retry" |
max_retries | No | Retry count when on_error = "retry" (default: 0) |
condition | No | Expression that must be truthy to execute phase |
Execution Order
- Phases with no
depends_onrun first (in parallel if multiple) - A phase starts only after ALL of its
depends_onphases complete - If a phase fails and
on_error = "fail", the entire workflow fails - If
on_error = "skip", the phase is marked skipped and dependents proceed without its output - If
on_error = "retry", the phase retries up tomax_retrieswith 1s, 2s, 4s backoff - Phases with
agent: "self"execute within the domain controller itself (for aggregation, validation, decisions) - If
approval: "required", execution pauses until approval is received before exposing results
Reference Expressions
Phase inputs use reference expressions to bind data from the task, previous phase outputs, entity context, or domain configuration:
| Root | Resolves To |
|---|---|
$task.payload.<key> | Field from the original TaskRequest.payload |
$task.context.<key> | Field from the TaskContext |
$phases.<name>.output | Full output map of a completed phase |
$phases.<name>.output.<key> | Specific key from a phase's output map |
$entity.<key> | Field from EntityContext (injected via task context) |
$config.<key> | Domain-specific configuration value (set at startup) |
Syntax
- Dot access:
$phases.scan.output.summary.score— traverses nested maps - Array index:
$phases.scan.output.files[0]— access by index (out of bounds →null) - Array expansion:
$phases.scan.output.files[*].metadata.images— collects a nested field from every element into a flat array - Literal passthrough: strings without
$prefix are passed as-is (allows mixing references with constants)
Resolution Rules
- Expressions MUST start with
$— literals without$are passed through - Unresolvable references resolve to
null(not an error) - If a required input resolves to
null, the domain controller fails the phase with"unresolved reference" - No type coercion — the resolved JSON value is passed as-is
- Circular references are impossible by construction (
depends_oncreates a DAG)
Workflow Execution Flow
When a domain controller receives a task via POST /v1/execute:
Phase 1 — Select workflow Match task.action against workflow triggers If no match → return failed result: "unknown action" Phase 2 — Initialize execution Create WorkflowExecution record: id, workflow_name, domain_name, task_id status: "running", started_at: now() Phase 3 — Resolve execution order Topological sort of phases by depends_on Group into levels: L0 (no deps), L1 (depend on L0), etc. Verify no cycles (fail if circular dependency) Phase 4 — Execute levels For each level (L0, L1, L2, ...): For each phase in this level (concurrently): a. Check condition (if set) — skip if falsy b. Resolve input references from task + previous outputs c. If agent = "self": Execute domain's own HandleMessage Else: Look up agent in service directory Build AgentMessage (from, to, action, payload) Sign with domain's private key POST to agent_url + /v1/message Wait for response (with timeout) d. Store phase result e. If failed: apply on_error strategy Phase 5 — Aggregate Collect all phase results Build final TaskResult: status: "success" | "failed" | "pending_approval" summary, changes, observations, recommendations metrics: {phases_total, completed, failed, duration_ms} Sign result with domain's private key Phase 6 — Record Store WorkflowExecution with all phase results Emit observations and recommendations (for lifecycle) Return TaskResult
Agent Dispatch
When a domain dispatches work to an agent, it uses direct messaging (POST /v1/message),
not the orchestrator's task endpoint. This keeps the orchestrator as a coordinator and the domain as the executor.
- Look up agent in service directory (populated during registration and updated via
/v1/servicesbroadcasts) - Build
AgentMessage: from, to, type: "request", action, payload - Sign:
JSON.stringify({from, to, action, payload}) POSTtoagent_url + /v1/message- Parse response
AgentMessage - Extract
response.payloadas phase output - If agent unreachable:
- Check if channel exists → use channel
- Request new channel from orchestrator → retry
- If still unreachable → fail phase
Result Aggregation
The domain controller merges results from multiple agents into a coherent outcome:
- Collect all phase outputs
- Merge observations: concatenate, deduplicate by target + element
- Merge recommendations: concatenate, sort by priority, deduplicate
- Merge proposed changes: group by file path, apply in order
- Compute summary metrics: total observations, recommendations by priority, changes by action type, workflow duration
- Apply domain-specific validation: reject conflicting recommendations, enforce business constraints, apply quality gates
- Set overall status:
"success"if all phases completed and no approval needed"pending_approval"if any phase or the workflow requires approval"failed"if any critical phase failed
Standard Actions
Every domain controller MUST implement these actions:
| Action | Description |
|---|---|
execute_workflow | Primary entry point. Invoked by the orchestrator via POST /v1/execute. Runs a named workflow and returns aggregated results. |
get_status | Returns domain state: online/degraded, required agent availability, active and completed workflow counts. |
get_workflows | Returns full workflow definitions. Runtime introspection endpoint for the orchestrator, CLI, and external tools. |
workflow_history | Returns recent workflow executions with results. |
Custom actions are domain-specific and defined in each domain's blueprint file.
Entity Context
Domains operate within the context of a business entity. The EntityContext provides:
- Entity identity (name, type, industry)
- Market positioning and brand voice
- Target audiences and their pain points
- Competitors
- Core keywords and topics
- Digital assets
Every domain SHOULD use entity context to ground its decisions in business reality. For example,
an SEO domain uses keywords and audiences to evaluate whether page titles target the right terms.
Entity context is injected via TaskContext.entity on every task execution.
Configuration
Domains accept configuration at startup via environment variables (prefix WL_DOMAIN_)
or configuration files. Values are accessible in workflow reference expressions as $config.<key>:
WL_DOMAIN_MAX_ISSUES_PER_FILE=50 WL_DOMAIN_SCORE_THRESHOLD=70 WL_DOMAIN_MEASUREMENT_WINDOW=86400
Domain configuration is NOT part of the manifest or wire protocol — it is local to the domain process.
Port Convention
| Range | Assigned To |
|---|---|
9700–9709 | Domain controllers (SEO = 9700, future domains = 9701+) |
9710+ | Work agents |
9750+ | Infrastructure agents |
9800 | Orchestrator |
Reference Domains
Three reference domains ship with the specification:
SEO
Audits, recommendations, and automated fixes for search engine optimisation. Dispatches to
seo-analyzer and a11y-checker. Workflows: seo-audit, seo-optimize.
Content
Content analysis, metadata validation, and quality assessment. Dispatches to
content-analyzer and meta-checker.
Health
Uptime monitoring, performance auditing, and availability tracking. Dispatches to
uptime-checker and perf-auditor.
Implementation Notes
- Domain as agent: a domain controller implements the same 5 protocol endpoints as any agent. The workflow logic lives in
ExecuteandHandleMessage. - Generic workflow engine: the execution loop (resolve order, dispatch phases, collect results) is generic. A single implementation can serve any domain — only the workflow definitions and aggregation rules are domain-specific.
- Parallelism: phases at the same dependency level SHOULD execute concurrently.
- State persistence: workflow execution state SHOULD be persisted so that workflows survive restarts.
- Timeout cascade: a workflow-level timeout SHOULD be enforced in addition to per-phase timeouts.
- Idempotency: phases SHOULD be idempotent — if retried, the agent should produce the same output for the same input.
Verification Checklist
- Domain registers with
type: "domain"in manifest - Orchestrator tracks domain status based on
required_agentsavailability - Domain dispatches phases to agents via
POST /v1/message - Phases with
depends_onwait for dependencies before executing - Phases at the same level execute concurrently
- Phase outputs are resolvable via
$phases.<name>.outputreferences - Failed phases respect
on_errorstrategy (fail, skip, retry) agent: "self"phases execute within the domain controller- Results from all phases are aggregated into a single
TaskResult - Observations and recommendations are emitted per workflow
- Domain responds to
get_statuswith agent availability - Domain responds to
get_workflowswith available workflows WorkflowExecutionis recorded with all phase results- Workflow timeout cancels all remaining phases
- Approval gates pause execution until approval is received