Schema
This chapter describes the IpTool YAML types you author and the conventions that govern how they map to OTTO JSON.
Top-level typed instances
IpTool defines three top-level types. Every IpTool YAML file consists
of one or more named instances of these types, tagged with
!cfg.type:<TypeName>.
IpToolParameterDefinition of a single parameter (keyword). Authored as a top-level named instance whose name is the OTTO parameter name with
/as the segment separator (INS/COLL/NAID,DET/WIN1/NX, …) — see Parameter names below for why/and not.. Declared once in a*.keys.ipt.yamlfile; per-template overrides are expressed by!cfg.merge <PARAM_NAME>:blocks in*.tpl.ipt.yamlfiles.IpToolTemplateA Sequencer template. Authored as a top-level named instance whose name is the OTTO
templateName. Lists the parameter names it uses (by name only — noref/overrideswrapper) and optionally carries aphase1block withreference_targetsfor Phase-1 acquisition templates.IpToolIpMetadataInstrument-wide metadata: format, constraints, checklist, laser. Authored once per instrument as
ip_metadata: !cfg.type:IpToolIpMetadatain a*.meta.ipt.yamlfile. The conventional instance nameip_metadatais what the compiler looks up.
The full type definitions live in
ifw-iptool/lib/resource/schema/ifw/iptool/iptool.schema.yaml,
expressed as CII Config Service !cfg.typedef declarations.
Conventions
snake_case vs. camelCase
IpTool YAML uses snake_case field names throughout, matching the convention used by VLT/ELT instrument control-system configuration (also CII Config Service-based). The OTTO JSON schemata use camelCase. The compiler translates field names at emit time; authors only ever see the snake_case form.
Examples of the translation:
initial_value→initialValuetemplate_name(instance name) →templateName(OTTO field)reference_targets→referenceTargetsmax_length→maxLength
One thing that does not get snake-cased:
Enum values (
P1Setup,acquisition,PHO, …) are OTTO literals and pass through unchanged.
Parameter names (/ in YAML, . in OTTO)
OTTO parameter names are FITS-style dotted identifiers
(DET.WIN1.NX, TEL.TARG.ALPHA, …). The CII Config Service,
however, rejects ``.`` in instance names — and IpTool authors
parameters as typed instances. To work around this, IpTool YAML uses
/ as the parameter-name segment separator:
In YAML:
DET/WIN1/NX,TEL/TARG/ALPHAIn OTTO output:
DET.WIN1.NX,TEL.TARG.ALPHA
The compiler converts / to . at emit time. The conversion
applies to YAML instance names and references to instance names
(!cfg.merge X:, template parameters: lists). It does not
apply to string fields whose value happens to contain a parameter
name (such as the laser block’s checked_parameters) — those are
authored dotted, exactly as they will appear in OTTO output.
The lifecycle of one parameter
Before the detailed type reference, it is worth following a single
parameter all the way from author’s YAML to OTTO JSON. The example
is DET/READ/CLKIND (the CCD read-out mode keyword) taken from
the bundled TSTINS example.
What the compiler emits (templates/tstins_img_cal_bias.json)
After ifwIpToolCompiler, the same parameter appears inside its
Template Signature as:
{
"name": "DET.READ.CLKIND",
"type": "keyword",
"label": "CCD Read-out",
"tooltip": "CCD Read-out Setup: port, binning, gain",
"domain": {
"values": [
"100kHz,1x1,high",
"100kHz,2x2,high",
"200kHz,1x1,low",
"200kHz,2x2,low"
]
},
"initialValue": "200kHz,2x2,low",
"hide": ["P1Setup", "P1Observation", "ETC", "P2", "SEQ"]
}
Translations the compiler applied:
DET/READ/CLKIND→DET.READ.CLKIND(/→.on the instance name).initial_value→initialValue(snake_case → camelCase).keyword_values→values(the IpTool union field collapsed to OTTO’s plainvaluesarray).{string: '200kHz,2x2,low'}→"200kHz,2x2,low"(the inner IpTool union slot collapsed to the JSON scalar OTTO expects).
What OTTO requires of it
The emitted JSON is validated by ifwIpToolValidator against
KeywordParam.json (a slice of
ifw-iptool/lib/resource/schema/otto/ip_schemata/parameters/):
{
"type": { "const": "keyword" },
"domain": {
"required": ["values"],
"values": {
"type": "array",
"uniqueItems": true,
"minItems": 1,
"items": { "$ref": "#/$defs/Keyword" }
}
},
"initialValue": { "$ref": "#/$defs/Keyword" },
"required": ["domain"]
}
"Keyword": {
"type": "string",
"minLength": 1,
"maxLength": 40,
"pattern": "^[A-Za-z0-9_.:,/#+-]+$"
}
Two rules worth remembering because they cause most early-author errors:
``initialValue`` must be one of the ``values``. OTTO does not enforce this with a strict
enum, but every per-type IP schema pullsinitialValuefrom the sameKeyworddefinition the domain entries match. A value outside the declared set passes IpTool typedef checking and is caught only by the OTTO validator.Both domain entries and ``initialValue`` are restricted by the ``Keyword`` pattern (40 chars, no whitespace, limited punctuation). The full set is
[A-Za-z0-9_.:,/#+-].
Per-template override with !cfg.merge
A bias template wants the high-gain, fast-readout mode and a tighter hide-list. Rather than re-declaring the whole parameter, the template file overrides only what differs:
!cfg.include ipt/tstins/include/det.keys.ipt.yaml:
!cfg.include ipt/tstins/include/seq.keys.ipt.yaml:
!cfg.include ipt/tstins/include/dpr.keys.ipt.yaml:
!cfg.merge DET/READ/CLKIND:
initial_value: {string: '100kHz,2x2,high'}
hide: [P1Setup, P1Observation, ETC]
tstins_img_cal_bias: !cfg.type:IpToolTemplate
type: calib
instrument: TSTINS
label: TSTINS imaging bias
description: Bias frames for TSTINS imaging
parameters: [..., DET/READ/CLKIND]
The merge modifies the existing DET/READ/CLKIND instance (declared
in the included keys file) for this compilation unit only. Two
properties make this work cleanly:
Each
*.tpl.ipt.yamlfile is its own CII Config Service compilation unit. Merges intstins_img_cal_bias.tpl.ipt.yamldo not leak intotstins_img_obs_crsplit.tpl.ipt.yamleven though both files!cfg.includethe same keys aggregator. The compiler emits each template using its file’s post-merge parameter view, so the OBD for the bias template sees100kHz,2x2,highwhile the crsplit OBD sees the library default200kHz,2x2,low.!cfg.mergeis field-level: only the fields named under it are replaced, others inherit from the library entry. There is no per-field “deep merge”; settinghide: [P1Setup]replaces the whole list, not just the first element.
Two CII Config Service rules constrain !cfg.merge:
The merged instance must already be defined (typically via an included
*.keys.ipt.yaml). Merging into an undefined name fails at parse time withB0070_UNDEFINED_INSTANCE.Only existing fields can be modified — new fields cannot be introduced — and the new value must satisfy the field’s declared type. A type mismatch fails at parse time with
C0003_INVALID_VALUE.
Both errors point at the source file and line; chase those rather than the IpTool compiler output.
Failure modes mapped to where each is caught
The same parameter has several common ways to go wrong. The table below shows which layer catches each — useful when reading error messages.
Mistake |
Caught by |
Error code / message |
|---|---|---|
Typo in field name ( |
CII Config Service (parse time) |
|
Wrong inner type
( |
CII Config Service (parse time) |
|
|
|
|
|
|
|
|
CII Config Service (parse time) |
|
|
CII Config Service (parse time) |
|
The split between “parse time” (CII) and “post-compile”
(ifwIpToolValidator) is worth remembering: CII can verify
shapes (you wrote a keyword, so the domain must be
keyword_values) but cannot verify values (it does not know
KeywordParam.json’s Keyword pattern, nor that
initialValue must be a domain member). Those latter checks only
fire when the JSON reaches the OTTO validator.
Typed initial_value and domain
Both initial_value and domain are union types. Set exactly
one of their inner fields according to the declared parameter type
(e.g. initial_value: {integer: 1} for type: integer; the
compiler rejects mismatched combinations).
Per-type value constraints in OTTO
OTTO enforces extra constraints on certain parameter types beyond the
IpTool YAML schema’s type check. These apply to the
initial_value (and per-row value in OBDs). The two surprising
ones to keep in mind:
``ra`` and ``dec`` values are sexagesimal strings with at most 3 fractional-second digits:
rapattern:HH:MM:SSorHH:MM:SS.f/HH:MM:SS.ff/HH:MM:SS.fff(24 h range, 8-12 chars total).decpattern: same shape with an optional+/-sign,00to90degrees inclusive (8-13 chars total).
Values with more than 3 decimals (
10:00:00.123456) fail validation with a pattern-mismatch on the parameter’sinitialValue. This is an OTTOip_schemataconstraint, not an IpTool one. Authoring more than millisecond precision in an OB is not currently supported by the OTTO IP format.``keyword``
initialValuemust be a 1-10 character string that appears in thevaluesarray. Strings outside that array, or longer than 10 characters, are rejected.
If a value fails one of these checks the validator now reports the real pattern / enum violation (B5b fix); pre-fix the error would surface as a misleading “type X is not one of [‘binfile’, ‘textfile’]” via the closest-miss branch heuristic.