Guidance for AI coding agents working in this repository. Per-tool config files
(.claude/CLAUDE.md, .junie/guidelines.md) point here so there's a single source.
Humans should read development/README.md for a fuller quickstart.
Authoritative source of the openEHR REST API specifications plus three companion "smart formats" specs that ship in the same component. Two source flavours coexist:
- OpenAPI 3.0.3 YAML —
overview,system,ehr,query,definition,demographic,admin. - AsciiDoc for prose specs that don't describe HTTP surfaces:
simplified_formats,simplified_data_template,smart_app_launch.
manifest.json enumerates every spec (both flavours) for the openEHR publishing toolchain.
Bundled / rendered artifacts under computable/OAS/ and docs/*.html are committed —
they're the published deliverables. Latest release:
https://specifications.openehr.org/releases/ITS-REST/latest.
| Path | What |
|---|---|
specifications/<spec>.openapi.yaml |
Top-level OpenAPI entry per domain. Subfolders schemas/, operations/, parameters/, responses/, headers/, tags/, docs/ hold reusable refs. |
specifications/docs/<spec>/*.md |
Long description: prose, pulled into YAML via description: $ref: ./docs/<spec>/<File>.md. |
docs/<name>/*.adoc |
AsciiDoc sources for the three smart-formats specs (master.adoc + manifest_vars.adoc + chapters; smart_app_launch/diagrams/ for figures). The masters include:: {ref_dir}/docs/boilerplate/ from the specifications-base companion repo — not standalone. |
computable/OAS/<spec>-{codegen,validation,html}.openapi.yaml |
Build outputs. Committed. |
docs/<spec>.html |
Redoc-rendered HTML (OpenAPI specs) or Asciidoctor-rendered HTML (AsciiDoc specs). |
development/ |
All build/transform tooling. PHP + Node, Docker-only. PSR-4: OpenEHR\Specifications\Tools\OpenAPI\ under development/src/. |
manifest.json |
Component manifest (status, releases, Jira links). |
Everything runs in Docker — no local PHP/Node needed. Run from development/.
make bundle SPEC=<spec> performs four stages:
- Bundle —
redocly bundle→ singlecomputable/OAS/<spec>.openapi.json(transient). - Transform (PHP) —
bin/generate_allruns three writers underOpenEHR\Specifications\Tools\OpenAPI\Writer\:Codegen— for OpenAPI/Swagger generators. Strips internal schema name prefixes (regex/^UM?[A-Z][a-z][\w]+/,/^See?[A-Z][a-z][\w]+/) from schemas and$refs, dropsformat: uuid(some generators choke), and walks the_typediscriminator chain to populatediscriminator.mappingrecursively.Validation— slimmer, validation/mock-server variant.Html— extendsValidation; for Redoc rendering.
- Re-emit YAML — Redocly converts each transformed JSON back to YAML
(
-validationis bundled with--remove-unused-components). - Render —
redocly build-docs→docs/<spec>.htmlviaredoc-template.html.
cebe/php-openapi is the in-memory model; AbstractWriter owns load → parse →
mutate components.schemas → serialize.
| Task | Command |
|---|---|
| First-time setup | make install |
| Bundle one / all specs | make bundle SPEC=ehr (alias make ehr) / make all |
| Lint one / all specs | make validate SPEC=ehr / make validate-all |
| Generate a client | make generate SPEC=ehr LANG=kotlin |
| Mock server (apisprout / prism) | make run SPEC=ehr / make run-mock SPEC=ehr |
| Composer command | make composer ARGS="update -W" |
| Clean transient JSON + codegen | make clean |
Valid SPEC: overview system ehr query definition demographic admin.
Valid LANG: see LANGUAGES in development/Makefile
(php go csharp java kotlin python swift5 typescript-fetch).
How the polymorphic openEHR Reference Model is encoded in OpenAPI:
- Inheritance via
allOf. Concrete types extend parents throughallOf: [ $ref: ../<parent>.yaml ]. Abstract RM types (DATA_VALUE,LOCATABLE,PATHABLE,CONTENT_ITEM,ITEM(_STRUCTURE),DV_ORDERED,DV_QUANTIFIED,DV_AMOUNT, the syntheticVersionable, …) get their own schemas so theCodegenwriter can walk the chain. - Polymorphism via
oneOf+ discriminator. Every abstract root reachable by reference has a siblingU…union (UContentItem,UItem,UItemStructure,UDataValue,UDvText,UDvUri,UDvEncapsulated,UPartyProxy,UVersionable,UParty, …) listing concrete subtypes inoneOfwithdiscriminator.propertyName: _typeand an explicitmapping. UM…schemas are union members that pin_typeto a single enum literal so validators can dispatch.Update…schemas relaxrequiredfor write flows (carryuid, audit / contribution refs as needed).See…are documentation/cross-reference stubs.- Discriminator markers on every concrete type: declare both
x-discriminator-value: <RM_NAME>and a_typeproperty ofenum: [<RM_NAME>]. TheCodegenregexes rely on theU/UM/Seeprefixes — don't invent new ones. - No real generics. RM
DV_INTERVAL<T>,OBJECT_REF<T>,VERSION<T>,VERSIONED_OBJECT<T>are flattened into per-binding files (DvIntervalOfDate,ObjectRefOfHierObjectId,VersionOfComposition,VersionedComposition, …). Add a new binding rather than parameterising.
- Edit authoring sources, not rendered output.
computable/OAS/anddocs/*.htmlare regenerated. After changingspecifications/, re-runmake bundle SPEC=<spec>(ormake all) and commit the regenerated artifacts with the source change. For AsciiDoc specs, edit underdocs/<name>/and re-render via the openEHR Asciidoctor toolchain (handled outside this repo). - Long prose belongs in Markdown (
specifications/docs/<spec>/<File>.md, pulled via$ref). Reuse existing files in the schemas/operations/parameters/ responses/headers/tags subfolders before adding new ones. - PHP: PSR-4, PHP ≥ 8.3. After adding/renaming Writer classes, run
make composer ARGS="dump-autoload"beforemake all. - Never bypass discriminator/
$refrewriting inCodegen. Generators silently mis-emit polymorphic types ifdiscriminator.mappingis incomplete; the regex$refrewrites are what keep generated client class names readable. - Simplified Formats apply only where the data is mappable. When wiring
application/openehr.wt.flat+json/application/openehr.wt.structured+jsoninto an endpoint (viaAccept_LOCATABLE/ContentType_LOCATABLE), confirm every payload type has a mapping indocs/simplified_formats/master05-rm_mapping.adoc. Two CONTRIBUTION-specific rules (SPECITS-84):- EHR
CONTRIBUTIONaccepts the WT MIME types, but the format applies only toversions[].data(the embeddedCOMPOSITION/EHR_STATUS/FOLDER). The envelope (uid,versions[]metadata,audit) stays canonical JSON — document this scoping in any new related operation/response. - Demographic
CONTRIBUTIONdoes not support the WT MIME types —PERSON/ORGANISATION/GROUP/AGENT/ROLEhave no FLAT/STRUCTURED mapping upstream (Better, EHRbase) or insimplified_formats.
- EHR
- 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.
Re-check on every PR that touches schemas/:
- Missing concrete subtypes — every concrete descendant of an abstract RM root
must exist and be wired into the matching
U…union (bothoneOfanddiscriminator.mapping). - Missing mandatory properties —
is_mandatory:truein the BMM means the property must be in bothproperties:andrequired:. EHR-wide aggregates (EHR,EHR_STATUS,COMPOSITION,CONTRIBUTION,ITEM_TAG,DV_INTERVAL,DV_DURATION,DV_PROPORTION) are the historical hotspot. - Over-/under-typing of references — where RM narrows a type
(
EHR_STATUS.subject: PARTY_SELF), don't widen to the parent union; conversely, where RM uses an abstract parent, reference the union, not a specific subtype. - Conditional invariants ≠
required—is_persistent implies context = Voidmeans the property is optional on the wire; don't put such properties inrequired. - List cardinality
1..*→required:plusminItems: 1. Required alone allows an empty array. - Property name drift — historical schemas occasionally use names that don't match the canonical openEHR JSON serialisation. Renaming is a wire-format break — coordinate with EHRbase / Better.
- JSON Schema
patternis bare ECMA-262, not Perl. No/.../delimiters. - Don't silently re-declare inherited properties. Redefining in the child to
change the type makes Redocly and some generators mis-merge. Fix the parent or
flatten per-binding (mirroring how
DvIntervalOf*handlesDV_INTERVAL<T>).
- openEHR specifications portal — https://specifications.openehr.org/ (
RM,BASE,AOM2,ITS-REST). - BMM JSON in
specifications-RM/specifications-BASEis the canonical machine-readable model; it's the tie-breaker when prose and UML disagree. - EHRbase (https://github.com/ehrbase/ehrbase) and Better Care WebTemplate (https://github.com/better-care/web-template) for serialisation conventions in practice.
- If an openEHR type-specification MCP/lookup tool is available, prefer it for
single-class checks (e.g.
type_specification_get TYPE_NAME) over reading the HTML spec.
The smart-formats specs are openEHR adaptations of upstream work — cross-check before editing:
docs/smart_app_launch/- HL7 SMART App Launch IG — https://hl7.org/fhir/smart-app-launch/
- SMART Health IT — https://docs.smarthealthit.org/
docs/simplified_formats/anddocs/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
URLs were live-checked at last edit; verify before quoting and keep the bibliography
in each master.adoc in sync.
development/docker-compose.ymlpinsuser: 1000:1000. On hosts where your UID differs (Windows/WSL), remap or rebuild — otherwise volume writes are owned by uid 1000.- Redocly runs with
NODE_OPTIONS=--max-old-space-size=4048. On OOM duringmake all, close watchers and bundle one spec at a time. - Mock servers and the Redocly preview want port 8000 (prism also exposes 4010); free those before launching.
cleanremoves only transient JSON undercomputable/OAS/and thecodegen/tree — not published*.openapi.yamlartifacts.
Apache-2.0 (root LICENSE). Authoring stays in OpenAPI 3.0.3 (info.x-spec /
info.x-status per file). Release history and Jira project links: manifest.json.