Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 47 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,26 @@ How the polymorphic openEHR Reference Model is encoded in OpenAPI:
upstream (Better, EHRbase) or in `simplified_formats`.
- **No tests.** This project has none and none are expected; do not scaffold a test
framework.
- **Trace numbers in commits.** History references Jira tickets (`SPECITS-NN`) —
follow that style.
- **Commit messages: short subject, optional body, Jira ticket on the subject
line.** Follow GitHub-style conventions: a concise subject line (target ~70
characters, hard limit ~72) written in the imperative mood and capturing the
headline change. When a Jira ticket exists, reference it on the subject line
in the form `(SPECITS-NN)` (or the equivalent project key — `SPECPR`,
`SPECRM`, …) so `git log --oneline` stays scannable. If the change needs
more context — rationale, cross-references to upstream issues/Discourse
threads, lists of touched files, links to implementations consulted — put
it in the commit body, separated from the subject by a blank line. Reserve
the body for information that doesn't already live in the diff or the
amendment record; the amendment record stays terse (see the rule above)
precisely because the commit body carries the full story.
- **Keep amendment-record entries short and succinct.** `master00-amendment_record.adoc`
in each AsciiDoc spec is a one-line-per-issue ledger, not a changelog narrative.
Each entry should be a single sentence (or a short semicolon-separated list when
one ticket bundles related fixes), naming the headline change and the affected
RM/FLAT names in backticks. Keep the full rationale and worked examples in the
spec body and in the commit message / PR description — not in the amendment record.
When extending an existing unreleased entry rather than adding a new row, fold
the new headline into the existing list with a `;` separator and stop there.

### Recurring schema-alignment pitfalls

Expand Down Expand Up @@ -176,9 +194,33 @@ before editing:
- HL7 SMART App Launch IG — <https://hl7.org/fhir/smart-app-launch/>
- SMART Health IT — <https://docs.smarthealthit.org/>
- `docs/simplified_formats/` and `docs/simplified_data_template/`
- EHRbase SDT docs — <https://docs.ehrbase.org/docs/category/simplified-data-template-sdt>
- Better Care WebTemplate — <https://github.com/better-care/web-template>
- WebTemplate test suite — <https://github.com/better-care/web-template-tests>
- EHRbase SDT user docs — <https://docs.ehrbase.org/docs/category/simplified-data-template-sdt>
- EHRbase FLAT data-types reference (canonical examples for every RM type) —
<https://github.com/ehrbase/documentation/blob/master/source/09_flat/01_data_types/index.rst>
- EHRbase server source — <https://github.com/ehrbase/ehrbase>
- EHRbase openEHR SDK (Java; FLAT/STRUCTURED un/marshalling) —
<https://github.com/ehrbase/openEHR_SDK>; relevant subtree:
`serialisation/src/main/java/org/ehrbase/openehr/sdk/serialisation/`.
- Better Care WebTemplate (Kotlin reference implementation) —
<https://github.com/better-care/web-template>; the FLAT path-suffix conventions
are codified in two parallel trees:
- `src/main/kotlin/care/better/platform/web/template/converter/flat/mapper/<RmType>ToFlatMapper.kt`
— emits the FLAT keys/suffixes for each RM type.
- `src/main/kotlin/care/better/platform/web/template/converter/raw/factory/leaf/<RmType>Factory.kt`
— parses inbound FLAT keys back to RM (and lists tolerated input aliases).
- Better WebTemplate test suite (golden FLAT/STRUCTURED fixtures) —
<https://github.com/better-care/web-template-tests>
- openEHR Discourse (ITS / Platform categories) —
<https://discourse.openehr.org/c/specifications/its/41> for raised gaps,
e.g. <https://discourse.openehr.org/t/rm-mappings-dv-coded-text-preferred-term-missing/11780>.

For any Simplified-Formats edit that affects path suffixes, attribute names, or
mutex rules, the **two implementations are the tie-breaker** when prose and
intuition disagree: the FLAT path-suffix vocabulary the spec normatively documents
must match what Better's `*ToFlatMapper.kt` emits and what EHRbase's
`09_flat/01_data_types/index.rst` shows. Known intentional divergences from the
RM attribute names (`|mediatype` vs `media_type`, `|alternatetext` vs
`alternate_text`) are baked into both implementations and must be preserved.

URLs were live-checked at last edit; verify before quoting and keep the bibliography
in each `master.adoc` in sync.
Expand Down
9 changes: 7 additions & 2 deletions docs/simplified_formats/master00-amendment_record.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@

4+^h|*ITS_REST Release (unreleased)*

|[[latest_issue]]1.1.0
|[[latest_issue]]1.1.1
|{spec_tickets}/SPECITS-94[SPECITS-94^]. Clarified Level Removal (attribute elision vs wrapper collapse) and `EVENT` rule; added `\|other` and `\|preferred_term` suffixes on `DV_CODED_TEXT`; fixed `LINK` (`\|target`) and `DV_MULTIMEDIA` (`\|mediatype`) mappings; corrected `EVENT_CONTEXT` table and added `PARTICIPATION` section; corrected PARTY `\|id_namespace` RM path; documented PARTICIPATION `/relationship` for `PARTY_RELATED` performer; expanded `DV_PROPORTION` table (`\|precision`, `\|accuracy`, `\|accuracy_is_percent`; `\|normal_status` RM path); fixed `DV_DATE_TIME` / `DV_TIME` headers and bare-path notes; swept recurring `\|magnitude_status` and `\|normal_status` row corrections across `DV_COUNT` / `DV_DATE` / `DV_DATE_TIME` / `DV_TIME` / `DV_DURATION` / `DV_QUANTITY`; added `\|accuracy` / `\|accuracy_is_percent` to `DV_COUNT`.
|REST WG
|[[latest_issue_date]]28 Apr 2026

|1.1.0
|{spec_tickets}/SPECITS-61[SPECITS-61^]. Cleanup and consolidate Simplified Formats specification; Deprecating Simplified Data Template (SDT) specification
|REST WG
|[[latest_issue_date]]05 Nov 2025
|05 Nov 2025

4+^h|*ITS_REST Release 1.0.3*

Expand Down
97 changes: 81 additions & 16 deletions docs/simplified_formats/master04-basic_concepts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -843,32 +843,97 @@ Algorithm for converting structured format to flat:

== Level Removal

Certain RM types are omitted from paths to simplify the structure.
These types do not typically carry significant clinical information and would unnecessarily complicate the path structure.
Paths in Flat and Structured formats omit two distinct kinds of segments relative to the canonical RM path:

=== Always Removed
1. *Container attribute names* of `LOCATABLE`-to-`LOCATABLE` relationships are never present as path segments -- the parent connects directly to the child via the child's archetype node-id alias.
2. In addition, certain *wrapper node types* are themselves collapsed -- their archetype node-id is also dropped -- because they act only as structural slots and carry no clinically meaningful identity at that level.

The following node types are always removed from paths, but their respective data carrying attribute is taken as the value at that specific level:
=== Container Attributes Elided from Paths

* `ITEM_TREE` replaced by `ITEM_TREE.items`
* `ITEM_LIST` replaced by `ITEM_LIST.items`
* `ITEM_SINGLE` replaced by `ITEM_SINGLE.item`
* `ITEM_TABLE` replaced by `ITEM_TABLE.rows`
* `HISTORY` replaced by `HISTORY.events`
The following RM attribute names are universally absent from paths:

=== Conditionally Removed
* `COMPOSITION.content`
* `SECTION.items`
* `OBSERVATION.data`, `OBSERVATION.state`, `OBSERVATION.protocol`
* `EVALUATION.data`, `EVALUATION.protocol`
* `INSTRUCTION.activities`, `INSTRUCTION.protocol`
* `ACTION.description`, `ACTION.protocol`
* `ADMIN_ENTRY.data`
* `ACTIVITY.description`
* `HISTORY.events`
* `EVENT.data`, `EVENT.state`
* `ITEM_TREE.items`, `ITEM_LIST.items`, `ITEM_SINGLE.item`, `ITEM_TABLE.rows`
* `CLUSTER.items`

The following types are removed when they meet specific criteria.
=== Always Collapsed Wrapper Types

An `EVENT` node is removed when:
In addition to the attribute elision above, the following wrapper node types are themselves collapsed -- their archetype node-id is also dropped, so the parent connects directly to the wrapper's contents:

1. Its maximum occurrence is 1 (i.e., `max = 1`)
2. AND it has no sibling `EVENT` nodes in the same parent
* `ITEM_STRUCTURE` (abstract) and its concrete subtypes:
** `ITEM_TREE`
** `ITEM_LIST`
** `ITEM_SINGLE`
** `ITEM_TABLE`
* `HISTORY`

These types act as fixed structural slots in their parent (`ENTRY` for the `ITEM_STRUCTURE` family; `OBSERVATION` for `HISTORY`) and have no archetype node-id distinct from that slot.

=== Conditionally Collapsed Wrapper Types

An `EVENT` node is collapsed when both of the following hold:

1. Its maximum occurrence is 1 (i.e., `max = 1`), AND
2. No sibling `EVENT` nodes (of any concrete event type) exist in the same parent `HISTORY`.

`EVENT` nodes are retained when:

* Multiple `EVENT` types exist in the same `OBSERVATION` (e.g., `POINT_EVENT` and `INTERVAL_EVENT`)
* The `EVENT` can occur multiple times
* Multiple `EVENT` types exist in the same parent `HISTORY` (e.g., a `POINT_EVENT` alongside an `INTERVAL_EVENT`), or
* The `EVENT` can occur multiple times (`max > 1`).

=== Example

Take a single measurement magnitude inside a laboratory result panel. The canonical RM path is:

[source]
----
/content[openEHR-EHR-OBSERVATION.laboratory_test_result.v1]
/data[at0001]
/events[at0002]
/data[at0003]
/items[openEHR-EHR-CLUSTER.laboratory_test_panel.v1]
/items[openEHR-EHR-CLUSTER.laboratory_test_analyte.v1]
/items[at0001]
/value
----

The same field in Flat form:

[source]
----
laboratory_test_report/laboratory_test/laboratory_test_panel/laboratory_result:0/result_value|magnitude
----

Differences:

* Container attribute names (`content`, `data`, `events`, `items`, `value`) are elided wherever they appear.
* The `HISTORY` (`at0001`) and `ITEM_TREE` (`at0003`) wrapper nodes are collapsed; the `EVENT` (`at0002`) is conditionally collapsed (here `max=1`, single event type).
* The `OBSERVATION` and the two `CLUSTER` nodes are retained under their archetype-id aliases (`laboratory_test`, `laboratory_test_panel`, `laboratory_result`); the inner `CLUSTER` repeats, hence the `:0` instance index.
* `ELEMENT.value` is replaced by the `|magnitude` attribute suffix.


[[open_value_sets]]
== Open Value-Sets and the `|other` Suffix

When an archetype constrains a leaf to `DV_CODED_TEXT` with an _open_ value-set -- the bound terminology lists recommended codes but the constraint also accepts values outside the list (`listOpen: true` in the web template; non-`limit-to-list` in ADL) -- the same leaf may at runtime carry either a `DV_CODED_TEXT` (chosen from the list) or a `DV_TEXT` (free-text value).

Many archetypes deliberately combine a recommended coded list with free-text fallback (e.g. `state_of_dress`, `confounding_factors`, `overall_test_status`). Without a dedicated FLAT-level discriminator the `DV_CODED_TEXT` vs `DV_TEXT` branch would have to be inferred from value patterns, which is fragile and ambiguous. The `|other` suffix makes the branch explicit at the FLAT level and removes the need for receivers to reason about it.

* On *write*: clients supply the free-text value via `<path>|other: "<text>"`. The server persists the leaf as a `DV_TEXT` in the canonical RM (not as a `DV_CODED_TEXT` with empty `defining_code`).
* On *read*: when the leaf carries a `DV_TEXT` whose archetype constraint is `DV_CODED_TEXT` with `listOpen: true`, the server SHOULD emit it via `<path>|other: "<text>"` so that round-trip equality holds at the FLAT level.
* `|other` is mutually exclusive with `|code`, `|value` and `|terminology` on the same leaf path; servers MUST reject combinations.
* `|other` MUST be rejected when the constraint is closed (`listOpen: false`).

The per-suffix table is in the <<DV_CODED_TEXT>> class section.


== Validation
Expand Down
Loading