Skip to main content

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:

DomainWhat it does
llm-servingLLM inference server context: the serving-systems skill/references, /model weights, the accuracy + benchmark + reward-hack judge gates.
microservicesMicroservice workload context: service lifecycle, protocol correctness, and workload-specific evaluator guidance.
databaseDatabase / 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-writingCompute-kernel implementation and optimization against a task-owned reference, accuracy checker, and scored benchmark.
genericEmpty: 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.md is optional. Omit it and it's derived automatically by concatenating implementer.md and judge.md: no third copy to hand-maintain. Add it only when the single-agent ablation needs different framing.
  • orchestrator.md is 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-serving uses it for the continuous-batching/attention-kernel/CUDA-graph optimization floor.
  • profiler.md is 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:

VariableMeaning
modalityThe --modality value, or the domain's default_modality when the flag is omitted; None for domains that declare no modalities.
interfaceThe --interface value: inprocess (direct invocation inside an evaluator process) or service (over the wire). It does not imply a language or artifact format.
reference_pathPath to the reference material, or . when the input bundle has no reference/ directory.
benchmark_commandBenchmark command declared by the input manifest, or falsy if no benchmark is attached.
accuracy_commandAccuracy-checker command declared by the input manifest, or falsy if not attached.
runtime_notesRuntime-environment notes for the round.
profile_executionWhere profiling must run to observe the production hot path: local or remote.
workspace_sourcesTuple 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​

  1. Copy src/vibesys/domains/generic/ to a new in-repo src/vibesys/domains/<module_name>/ package, using underscores for the Python module name when the CLI domain name contains hyphens.
  2. Edit the copied package's prompts/README.md with its title and scope.
  3. Add implementer.md (what to read / what "done" means here) and judge.md (what to check) under the package's prompts/ directory. Leave a file out to inject nothing for that role.
  4. Optionally add single_agent.md for the --inner-loop single-agent ablation; omit it to derive it from the other two.
  5. Optionally add orchestrator.md and profiler.md when the neutral planning or profiling skeleton needs domain-specific examples or capture commands.
  6. Export a DEFINITION from the domain package's __init__.py, with prompt_dir=Path(__file__).resolve().parent / "prompts". If the domain needs setup/teardown behavior such as mounts or copy exclusions, implement EnvironmentHooks in that package and attach it to the definition.
  7. Register the definition in vibesys.domains.registry.DOMAINS.
  8. Add [agent].domain = "<name>" to the input manifest and run either uv run vibesys --outer-loop agent ... or uv 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.