Ioc Convert
Convert a builder XML IOC to ibek ioc.yaml, validate schema, generate runtime assets, and iterate until correct.
---
argument-hint: <path/to/IOC.xml> [<path/to/services-repo>]
description: Convert a builder XML IOC to ibek ioc.yaml, validate schema, generate runtime assets, and iterate until correct.
---
# IOC Convert and Validate Workflow
Convert builder XML `$1` to ibek `ioc.yaml` in the services repo `$2`, fix any
support YAML or converter issues, generate runtime assets, and verify `st.cmd`
and `ioc.subst` are correct.
This command uses **parallel subagents** for support YAML creation and error
fixing, to keep the main context small and speed up large IOCs.
---
## Phase 0 — Create isolated EPICS_ROOT (main agent)
Create a unique temporary directory for this agent's ibek state. This ensures
multiple `/ioc-convert` invocations can run in parallel without conflicting
over `/epics/runtime/`, `/epics/ibek-defs/`, or `/epics/ioc/`.
```bash
export EPICS_ROOT=$(mktemp -d /tmp/epics-ioc-convert-XXXXXX)
All subsequent ibek and ./update-schema commands in this session (and in
subagent prompts) must have EPICS_ROOT set to this value.
Phase 1 — Setup and XML conversion (main agent)
1a. Resolve the services repo
Follow services-repo-resolution.md
using $2 (if provided) or the IOC prefix from $1.
- If
INSTANCE_DIRalready exists:- Check
$INSTANCE_DIR/values.yaml— if it containsdev-c7:, this is a legacy IOC. Remove the entire folder (rm -rf $INSTANCE_DIR) and recreate it from.ioc_template(fall through to the "does not exist" case below). - Otherwise, if
config/ioc.yamlis present: rungit -C <services-repo> status— if ioc.yaml is modified or untracked, ensure it is committed or stashed before overwriting.
- Check
- If
INSTANCE_DIRdoes not exist:- If
<services-repo>/services/.ioc_template/exists:cp -r <services-repo>/services/.ioc_template/ $INSTANCE_DIR - Otherwise:
mkdir -p $INSTANCE_DIR/config/
- If
1b. Resolve support module paths
Read the _RELEASE file next to the XML following
find-module-path.md. Record a module path
table mapping module name → resolved path, e.g.:
ethercat → /dls_sw/prod/R3.14.12.7/support/ethercat/7-2
rackFan → /dls_sw/prod/R3.14.12.7/support/rackFan/2-12
This table will be passed to subagents in Phases 2 and 4.
1c. Run the conversion
Derive a terse description of the device this IOC controls from the XML filename and its contents (e.g. "Geobrick 06", "Vacuum system", "Pilatus detector"). Keep it short — a few words, no full sentences.
uv run builder2ibek xml2yaml $1 --yaml $INSTANCE_DIR/config/ioc.yaml --description "<description>"
1d. Review ioc.yaml and fix converters
Read the generated ioc.yaml. Do not read large referenced files yet —
that work is delegated to subagents in Phase 2.
Scan quickly for converter issues that must be fixed before support YAML work:
Spurious name: fields — follow the name parameter rules in
support-yaml-rules.md. When in doubt,
keep name: type: id. If a converter is needed, create
src/builder2ibek/converters/<module>.py (auto-discovered, no registration).
Attributes needing transformation — values that need renaming, numeric transformation, or removal before ibek sees them. Add or update the converter rather than editing ioc.yaml by hand.
dlsPLC entities — read docs/reference/dlsplc-migration.md and check the "What requires manual attention" table. Every case listed there should be fixed by implementing or improving the relevant converter.
Converters that emit multiple entities — when a single XML element must
produce more than one ibek entity, use entity.add_entity(extra) and
entity.delete_me(). Critical: pass plain dict objects to
add_entity(), never Entity(...).
Auto-substitution entities (auto_*) — entity types like
module.auto_xxxx correspond to a database template xxxx.template in the
db/ directory of the support module. These need a support YAML entity model
with just parameters and a databases section — no pre_init or post_init.
XML template entities — some builder modules use XML template files (the
entity class inherits from Xml in builder.py) that expand into many child
entities from other modules. Examples: SR-VA.d2PumpCart, SR-VA.gaugeSet.
These should not be expanded by a converter into individual child entities.
Instead:
- Keep the single template entity in
ioc.yamlwith only the macro parameters (e.g.dom,plc_ip,ts_ip). - Create a support YAML entity model that uses
sub_entitiesto instantiate the child entities from the existing entity models of the other modules (ether_ip, asyn, dlsPLC, etc.), passing the macro parameters via Jinja2 templating. This re-uses the existing entity definitions rather than duplicatingpre_init/databaseslogic. - Do NOT create a converter that expands the XML — the support YAML handles everything.
To build the sub_entities list: read the XML template file from the module's
etc/makeIocs/ directory, and for each non-commented child element, add a
corresponding sub_entity entry using the existing entity type. Hardcoded
values from the template become literal values in the sub_entity; macro
references like $(dom) become {{ dom }} Jinja2 expressions.
ethercat IOCs — the ethercat support module requires migration to
fastcs-catio and cannot be converted with a standard support YAML. If an IOC
uses ethercat entities, emit a TODO stub in the ioc.yaml and report to the
user that it needs manual migration. Do not attempt to create an ethercat
support YAML.
After any converter change: re-run xml2yaml and repeat 1d until the ioc.yaml is clean of converter-fixable issues.
1e. Enumerate modules needing support YAMLs
Parse the final ioc.yaml. For each entity type `
Maintain Ioc Convert?
Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.
[Ioc Convert on getagentictools](https://getagentictools.com/loops/epics-containers-ioc-convert-and-validate-workflow?ref=badge)