Domains: pointing vibesys at your problem space
A domain bundles the cross-cutting context the agents need for whatever you're building: the background knowledge the implementer must read, the correctness/performance/integrity gates the judge must enforce, profiling workflow details, and the same for the single-agent ablation. It's the answer to "what kind of system is this, and what does 'good' mean here?": kept separate from the neutral prompt skeleton.
Pick one in the input bundle manifest:
[agent]
domain = "llm-serving"
domain accepts a registered repo domain name:
| Domain | What it does |
|---|---|
llm-serving | LLM inference server context: the serving-systems skill/references, /model weights, the accuracy + benchmark + reward-hack judge gates. |
microservices | Microservice workload context: service lifecycle, protocol correctness, and workload-specific evaluator guidance. |
database | Database / dataflow engine context: in-place optimization of a vendored engine, judged by output-equivalence against a pristine round-0 copy of the same engine. |
kernel-writing | Compute-kernel implementation and optimization against a task-owned reference, accuracy checker, and scored benchmark. |
generic | Empty: no domain prose injected. The neutral baseline; copy it to start your own. |
Anatomy of a domain package
Each domain owns its Python definition and prompt assets under
vibesys/domains/<domain>/. Shared rendering and templates live in
vibesys/prompts/; strategy-specific templates live with their strategy.
Domain-specific environment setup/teardown code stays next to the definition.
src/vibesys/domains/my_domain/
__init__.py # exports DEFINITION
hooks.py # optional domain-specific EnvironmentHooks implementation
prompts/
README.md # optional human documentation
implementer.md # the implementer prompts' domain section
judge.md # the judge prompts' domain section
single_agent.md # the single-agent round prompt's domain section (optional)
orchestrator.md # the planner prompts' domain section (optional)
profiler.md # the profiler prompts' domain section (optional)
Rules:
- Inside the domain prompt directory, the filename is the address. The run's plan renders
each role file once into
domain_sections[<role>], and every strategy's prompt for that role renders it in one fixed slot, right after the run context (prompts/core_protocol/domain_section.j2). A strategy's own templates never name a domain. - A missing role file injects nothing for that role.
single_agent.mdis optional. Omit it and it's derived automatically by concatenatingimplementer.mdandjudge.md: no third copy to hand-maintain. Add it only when the single-agent ablation needs different framing.orchestrator.mdis optional. Omit it to inject nothing into the planner prompt (its neutral skeleton still applies). Add it to give the planner domain-specific strategy:llm-servinguses it for the continuous-batching/attention-kernel/CUDA-graph optimization floor.profiler.mdis optional. Omit it to use only the selected profiler's neutral mechanics. Add it when the domain needs a specific capture recipe, server startup contract, benchmark shape, or remote profiling entry point.- Write normal Markdown prose. The base template owns the surrounding structure (task, pass criteria, workspace, output contract); your role file owns the domain content.
Branching on the run (optional Jinja)
Role files are rendered with Jinja, so you can branch on the run's context. Most domains never need this: reach for it only when a gate depends on what's attached to the run. Every role file gets the same variables, so you can use any of these in any file without tracking which role you're in:
| Variable | Meaning |
|---|---|
modality | The --modality value, or the domain's default_modality when the flag is omitted; None for domains that declare no modalities. |
interface | The --interface value: inprocess (direct invocation inside an evaluator process) or service (over the wire). It does not imply a language or artifact format. |
reference_path | Path to the reference material, or . when the input bundle has no reference/ directory. |
benchmark_command | Benchmark command declared by the input manifest, or falsy if no benchmark is attached. |
accuracy_command | Accuracy-checker command declared by the input manifest, or falsy if not attached. |
runtime_notes | Runtime-environment notes for the round. |
profile_execution | Where profiling must run to observe the production hot path: local or remote. |
workspace_sources | Tuple of the manifest's [[workspace.sources]] entries (pinned starting-point checkouts materialized into the workspace; each has name, repo, commit, dest), or empty when the input bundle declares none. Branch on it to swap from-scratch guidance for build-on-the-seed guidance. |
A domain that supports modalities declares them on its DomainDefinition (modalities,
default_modality) and keeps one partial per role under prompts/modality/<name>/<role>.md,
which its role files include. A --modality the domain does not declare is rejected with an
error naming the choices.
These are always defined (falsy when not applicable), so a plain {% if benchmark_command %}
is enough: no is defined guard needed.
Example (inside judge.md):
## Correctness gates
1. `pytest` passes.
{% if benchmark_command %}
2. Run `{{ benchmark_command }}` and confirm it succeeds.
{% endif %}
How to add a domain
- Copy
src/vibesys/domains/generic/to a new in-reposrc/vibesys/domains/<module_name>/package, using underscores for the Python module name when the CLI domain name contains hyphens. - Edit the copied package's
prompts/README.mdwith its title and scope. - Add
implementer.md(what to read / what "done" means here) andjudge.md(what to check) under the package'sprompts/directory. Leave a file out to inject nothing for that role. - Optionally add
single_agent.mdfor the--inner-loop single-agentablation; omit it to derive it from the other two. - Optionally add
orchestrator.mdandprofiler.mdwhen the neutral planning or profiling skeleton needs domain-specific examples or capture commands. - Export a
DEFINITIONfrom the domain package's__init__.py, withprompt_dir=Path(__file__).resolve().parent / "prompts". If the domain needs setup/teardown behavior such as mounts or copy exclusions, implementEnvironmentHooksin that package and attach it to the definition. - Register the definition in
vibesys.domains.registry.DOMAINS. - Add
[agent].domain = "<name>"to the input manifest and run eitheruv run vibesys --outer-loop agent ...oruv run vibesys --outer-loop evolve ...from the repository root.
Domains are registered explicitly so prompt context, environment hooks, and tests stay tied to the same domain identity.
Kernel-writing tasks and skills
Set the domain in the task's input bundle:
[agent]
domain = "kernel-writing"
The bundle also supplies its objective, candidate contract, reference material, accuracy checker, and benchmark. This domain provides role guidance; those task files define the actual API, supported inputs, tolerances, and score.
The preset resources/skills/ root includes kernel-correctness,
kernel-performance, kernel-ncu-analysis on CUDA, and kernel-rocprof-analysis
on ROCm. Their sidecar metadata
loads them only for kernel-writing. To add task-specific skills while keeping
these presets, pass --extra-skills PATH. Use --skills-dir PATH to replace the
presets entirely. See CLI skill flags for the loading
rules.
--profiler auto selects NCU on CUDA and rocprof on ROCm when the run
environment supports it, and none on other backends. Use --profiler none to
skip profiling. NCU is unavailable for this domain on other compute backends and
is not available to other domains.
Scope
Domains cover implementer + judge + profiler (+ single-agent + orchestrator)
context, including language and tooling requirements shared by that problem
space. More specific callable APIs, ABIs, artifacts, ownership rules, and
service protocols belong to the input bundle's objective and candidate-contract
documentation. The --interface mode describes only the process boundary.