This is a concept sketch, not an implementation plan.
The core idea: Jido.Domain is the missing authoring layer above Jido.Pod.
Jido already has strong primitives:
Jido.Agentfor behaviorJido.Podfor durable topology- signals/directives for protocol and effects
Jido.AgentServerandInstanceManagerfor runtime lifecycle
What still feels incomplete is the application-level object a developer is actually building.
Today, a serious multi-agent app usually ends up inventing its own shape:
- one module for the public API
- one module for the pod
- a few policy/helper modules
- a few projection/artifact modules
- a rationale for when to use signals directly vs when not to
That works, but it is all convention and no named abstraction.
So the developer experience still has a gap:
- what is the root object?
- where do I put startup logic?
- where do I put app-facing commands and queries?
- how should Phoenix or another host app interact with a pod-backed runtime?
Introduce Jido.Domain as a higher-level authoring construct.
Not a new runtime.
Not a replacement for Pod.
Not a replacement for Agent.
Instead:
Domainis the public app-facing boundaryPodis the durable runtime backing that domainAgentremains the unit of behavior inside the pod- signals remain the internal protocol
The intended stack becomes:
Phoenix / Host App -> Domain -> Pod -> Agent
Or, in AgentOS terms:
Phoenix / Host App -> Domain -> AgentOS Kernel -> Pod -> Agent
That means:
- the host app should call a domain module
- the domain module should wrap pod operations in semantic commands/queries
- the pod should remain the durable runtime unit underneath
- signals should still matter, but they should not be the only public API
Signals are still the right protocol for:
- ingress events
- inter-agent communication
- child-to-parent communication
- external dispatch and routing
But signals are not a great host-app API by themselves because they expose:
- transport details
- signal naming conventions
- routing details
- multi-step choreography
A Phoenix app generally wants to call:
IssueTriage.open_run/1IssueTriage.ingest_issue/2IssueTriage.status/1RepoWorkspace.sync/1SupportDesk.submit_ticket/2
So the rule should be:
- signals are the internal protocol
- domain commands and queries are the public API
A Domain should own:
- the public command/query API
- orchestration across multiple pod/runtime calls
- ingress translation from product events into pod protocol
- policy logic or policy modules
- workflow artifacts / projections / memory-thread shaping
- lifecycle hooks such as fresh-run initialization and shutdown cleanup
A Domain should not replace:
- pod topology
- agent behavior
- kernel boot and persistence
This is not intended to compile. It is a sketch of the authoring experience.
defmodule MyApp.IssueTriage do
use Jido.Domain,
name: "issue_triage",
manager: :issue_triage_runs
commands do
command :open_run, params: [run_id: :string]
command :ingest_issue,
params: [
issue_id: :string,
repo: :string,
title: :string,
body: :string,
labels: {:list, :string}
]
command :publish_run
end
queries do
query :status
query :timeline
query :artifacts
query :topology
end
lifecycle do
mount do
{:ok, %{definition_id: "triage/v1"}}
end
shutdown do
:ok
end
end
pod do
agent :triager, MyApp.IssueTriage.Agents.Triager,
registry: :triager_agents,
activation: :eager
agent :researcher, MyApp.IssueTriage.Agents.Researcher,
registry: :research_agents,
activation: :lazy,
depends_on: [:triager]
agent :reviewer, MyApp.IssueTriage.Agents.Reviewer,
registry: :review_agents,
activation: :lazy,
depends_on: [:triager, :researcher]
# Owned nested pod
pod :repo_analysis, MyApp.RepoAnalysis,
registry: :repo_analysis_runs,
activation: :lazy,
key: &"repo:" <> &1.repo
# External reference only
ref :knowledge_base, MyApp.KnowledgeBase,
registry: :knowledge_base_runs,
key: & &1.repo
end
ingress do
sensor MyApp.Sensors.GitHubIssueWebhook,
as: :github_issue_webhook,
config: %{source_path: "/webhooks/issues"}
end
policy do
def classify(title, body, labels) do
cond do
"bug" in labels -> :bug
String.contains?(String.downcase(body), "unknown root cause") -> :bug
true -> :feature
end
end
def requires_research?(body, labels) do
"needs-research" in labels or
String.contains?(String.downcase(body), "unknown root cause")
end
end
artifacts do
record :issue_ingested,
memory: [world: :current_issue, events: {:append, %{kind: :issue_ingested}}],
thread: {:append, %{kind: :issue_ingested}}
record :triage_completed,
memory: [world: :triage, events: {:append, %{kind: :triage_completed}}],
thread: {:append, %{kind: :triage_completed}}
end
routes do
on "issue.webhook.received" do
project :issue_ingested
send_to :triager, "issue.triage.requested"
end
on "triage.completed" do
project :triage_completed
if policy(:requires_research?) do
ensure :researcher
send_to :researcher, "issue.research.requested"
else
ensure :reviewer
send_to :reviewer, "issue.review.requested"
end
end
end
end- One obvious root object answers: "what am I building?"
- Phoenix can call a stable domain API instead of raw pod mechanics.
- Policy and artifact logic get real homes.
- Signals remain important, but they become the internal wire protocol.
- The
Agentnaming tension gets easier:Agentstill means runtime behavior primitive in JidoDomainbecomes the thing an app author is actually building
This distinction feels important:
An owned nested pod.
- this domain is responsible for acquiring it
- this domain may reconcile it as part of its workflow
- it participates in the domain's runtime composition
A referenced external pod.
- this domain can signal/query it
- this domain does not own its lifecycle
That keeps composition and integration separate.
I think Domain is likely too opinionated to land in core Jido immediately.
The more natural incubation point may be one layer above core, especially in something like AgentOS, where the stack is already:
- host app
- kernel
- pod
- agents
In that world, Domain becomes the missing application authoring layer.
- Should the name be
Jido.Domainor something more specific likeJido.AgentOS.Domain? - Should v1 be a thin macro, or go straight to a real DSL?
- Should
policyandartifactsbe DSL sections or just nested modules by convention? - Should nested
podand externalrefbe in v1 or come later? - Should the public API be generated commands/queries, or mostly hand-written functions with a little help underneath?
- If this becomes a serious DSL, should it use Spark?
Jido.Domain is an attempt to formalize the thing developers are already building informally:
- a public domain API
- backed by a durable pod
- composed of agents
- integrated into a host app like Phoenix
That feels like the missing end-to-end story.