Template Authoring
Conflict Resolution
When guides conflict, apply this priority (highest first):
- Rules and syntax specifications
- Idioms and structural constraints
- Principles
- Convenience
Step 1: Load Guides (MANDATORY)
Before any template work, load the authoritative guides:
guide_get("openehr://guides/templates/principles")
guide_get("openehr://guides/templates/rules")
Load additional guides as needed:
guide_get("openehr://guides/templates/oet-syntax")— OET authoring syntaxguide_get("openehr://guides/templates/oet-idioms-cheatsheet")— common OET patternsguide_get("openehr://guides/templates/cgem-framework")— full CGEM dataset-splitting framework (Step 6)guide_get("openehr://guides/templates/opt-structure")/guide_get("openehr://guides/templates/web-template")— runtime forms (OPT, web template) when discussing deployment or FLAT/STRUCTURED paths
Step 2: Research Before Creating
Search for existing templates first:
ckm_template_search("<use-case>")
If creating a new template, search for archetypes to include:
ckm_archetype_search("<concept>")
When CKM comes up empty, published-on-GitHub project content is a secondary channel — repositories tagged with the openehr-content topic (search topic:openehr-content, ~14 repos: freshEHR, Apperta-CKM projects, regional programmes, individual modellers). CKM holds relatively few templates, so this is more often useful for templates than for archetypes. Treat finds as leads, not governed artefacts: unlike CKM there is no editorial review, no reliable lifecycle_state, and no integrity guarantee, so cite the repo and commit you looked at, never present a find as "published". This needs web access (WebSearch/WebFetch/gh), which this skill does not hold — ask the main session to run the search.
Step 3: Use-Case Specificity
Templates target particular clinical workflows. Define the use-case clearly:
- What clinical scenario does this template serve? (e.g., discharge summary, vital signs form, medication reconciliation)
- What data points are required vs optional?
- Who will use it? (clinician, nurse, admin)
Step 4: Archetype Aggregation
Selecting Archetypes
- Choose archetypes that precisely fit the use-case
- Minimize archetype count — each should serve a clear purpose
- Prefer well-established CKM archetypes over custom ones
COMPOSITION Structure
Templates are rooted in a COMPOSITION archetype. Nest entry archetypes (OBSERVATION, EVALUATION, INSTRUCTION, ACTION) and CLUSTER archetypes within it.
Use type_specification_get to verify COMPOSITION structure when needed.
Step 5: The Narrowing Principle
Templates constrain archetypes — they NEVER expand:
- Mandatory stays mandatory: Cannot make required fields optional
- Optional can become mandatory: Can set
min=1on optional fields - Optional can be excluded: Set
max=0to hide fields - Value sets only narrow: Can restrict coded text options, never add new ones
- Cardinality only narrows: Can reduce max occurrences, never increase beyond archetype definition
- Tightening unconstrained RM attributes is allowed: constraining an RM attribute the archetype left open is still narrowing
A template's four jobs (Archetype Technology Overview): composition (fill slots), element choice (remove/mandate/leave optional), narrowing, and setting defaults.
Defaults vs assumed values
Set a default value (OET: default="..." on a <Rule>) where the use case fixes or strongly implies a single value (e.g. setting, patient position). Defaults appear in the recorded data; archetype-level assumed values are semantic fallbacks for omitted optional items and do not appear in the data — never confuse the two.
Step 6: CGEM Framework
Use CGEM (freshEHR's analysis method — a design aid, not an openEHR specification) to decide how a dataset splits across templates, and to set each template's composition category. Load guide_get("openehr://guides/templates/cgem-framework") for the definitions, mapping table and caveats:
| CGEM category | Data nature | COMPOSITION.category |
Versioning behaviour |
|---|---|---|---|
| Global Background | True across all contexts for the patient's whole life (allergies, problem list, CPR/ReSPECT decision, current medications) | persistent (431) |
One current version per patient, updated in place |
| Contextual Situation | Single source of truth for one care journey / episode / condition (diagnosis-and-staging summary, condition care plan) | episodic (451) |
One current version per journey; a new journey creates a new instance |
| Event Assessment | Discrete, repeated recordings at a point in time (vitals at a visit, lab result, assessment score) | event (433) |
New composition per submission; never overwritten |
| Managed Response | Formal order/fulfilment cycle tracked from request to completion (referral, prescription, investigation request) | not a category code — usually event (sometimes persistent) |
Order state tracked across ACTIONs via the ISM |
Applying it: inventory the datapoints → categorise each C/G/E/M → group same-category datapoints into candidate templates → set each template's category to match → decide reuse (Global Background is usually already modelled and often only read by the form; Event templates are prime reuse candidates) → confirm Managed Response items genuinely need INSTRUCTION/ACTION + ISM and downgrade the rest to simple records.
Three things to state explicitly in any split report:
- Four CGEM categories, three category codes — Managed Response is not a
COMPOSITION.category; it is anevent(orpersistent) composition distinguished by its INSTRUCTION/ACTION entries and the ISM. 451 episodicis normative but unevenly implemented — confirm the target platform supports it;persistentplus governance conventions is the common fallback.- One form commonly spans several compositions across several categories, so a single form rarely means a single template.
Step 6b: Template from a form (inverse workflow)
When the starting point is a clinical form to implement rather than a template design, load references/template-from-form.md and follow it: parse the form into a field inventory, run the CGEM dataset split (Step 6) first, then sketch each implied template — archetypes to aggregate, RM entry type per field group, narrowing notes. Step 1's mandatory guide loads still apply in this mode (plus the CGEM guide — the reference covers it). The output is a design sketch, never OET XML; once the user confirms the sketch, continue at Step 8b to emit the file.
Step 7: Terminology in Templates
- Prefer DV_CODED_TEXT over free text where possible
- Constrain value sets to the local clinical context
- Use
terminology_resolveto verify openEHR terminology bindings inherited from archetypes — openEHR terminology only; it errors on external codes, so check SNOMED CT / LOINC / ICD bindings against the archetype's ownterm_bindingsrubrics instead
Step 8: The four serialisations
One design intent, four serialisations at three layers — not interchangeable, and only OET is hand-authorable:
| Format | Layer | Purpose |
|---|---|---|
OET (.oet) |
source | Authoring format — human-editable XML referencing archetypes plus narrowing. The artefact to version |
Archetype Designer .t.json |
source | AOM2 differential template JSON (@type: TEMPLATE, parentArchetypeId, differential: true, templateOverlays) — the JSON analogue of OET, from Better's Archetype Designer ("Export Fileset"). Tool-managed: read and review it, but make design edits in the tool or in an OET |
OPT (.opt/.optx/.optj) |
compiled | Operational Template — flattened, self-contained runtime artefact the CDR validates against (XML in ADL 1.4 practice; OPT2 adds ADL/XML/JSON, and raw vs profiled variants). Generated, never hand-authored |
| Web Template (JSON) | derived runtime | Better/EHRbase simplified projection of the OPT for UI generation; its node ids define the FLAT/STRUCTURED path schema. Derived, lossy, never authored |
OET (or .t.json) + archetypes ──► OPT ──► web template
.t.json is not a web template — it sits at the source layer with slots intact, while a web template is flattened runtime JSON with aqlPath/inputs. Both come from Better, at opposite ends of the pipeline; "the Better template" is ambiguous, so always name which one.
For when each format is hand-authorable vs tool-generated and what checksums each carries, load guide_get("openehr://guides/templates/serialization-formats"); for the runtime forms in depth, guide_get("openehr://guides/templates/opt-structure") and guide_get("openehr://guides/templates/web-template"). Reference syntax guides:
guide_get("openehr://guides/templates/oet-syntax")
guide_get("openehr://guides/templates/oet-idioms-cheatsheet")
Step 8b: Emit the OET
Produce a real, slot-correct OET — not just a design sketch. (The from-form mode in Step 6b produces the sketch; this step turns a confirmed design into the file.) Following templates/oet-syntax:
- Root
<template>with a root COMPOSITION archetype reference and a fresh<id>(see UID note below). - A
<Content>entry per included archetype, nested to mirror the COMPOSITION → SECTION → ENTRY → CLUSTER structure. <Rule path="…">elements for each narrowing constraint (min/max,limitToList, unit hardening,hide_on_form, label overrides,default="…"for use-case-fixed values) — respecting the narrowing principle (Step 5).- A trailing
<Context>if the design needs composition context (e.g./context/settingfixed to a code).
There is no automated OET/OPT validator available, so validate manually against the loaded templates/oet-syntax guide and the Step 9 checklist; state any unconfirmed constraint rather than asserting validity.
Step 9: Quality Review
Run through the quality checklist:
guide_get("openehr://guides/templates/checklist")
Verify:
- Clear use-case definition
- Appropriate archetype selection
- Narrowing principle respected (no expansions)
- Required fields marked correctly
- Excluded fields set to max=0
- Terminology constraints appropriate for context
- Value sets verified (quantity constraints, unit hardening, "limit to list" coded text)
- Defaults set where the use case fixes a single value (setting, patient position); no assumed-value confusion
- Annotations and UI hints appropriate (hide_on_form, contextual label overrides)
- Valid OET syntax
Output
Generate valid OET files. Use the Write tool to create .oet files in the appropriate project location.
Identifiers and checksums
- A new template needs a fresh
uid— mint a random UUID (v4). If a shell is available,uuidgen(orpython3 -c 'import uuid; print(uuid.uuid4())') works; otherwise generate the UUID directly (this skill has noBashtool, so don't assume shell access). - Do not hand-write build checksums (
MD5-CAM-*,build_uid) — they are tool-computed; a missing/stale checksum is advisory and never blocks authoring the OET.