Skip to content

Move the metadata that must not be changed into the base content package - #13

Open
dkayiwa wants to merge 4 commits into
mainfrom
move-core-metadata-out-of-demo
Open

Move the metadata that must not be changed into the base content package#13
dkayiwa wants to merge 4 commits into
mainfrom
move-core-metadata-out-of-demo

Conversation

@dkayiwa

@dkayiwa dkayiwa commented Aug 12, 2026

Copy link
Copy Markdown
Member

Reworked after @ibacher's review. The first pass used the wrong test — "needed to run O3" — and moved out more than it should have. Now narrowed to must be set this way. 43 files → 28.

The test used

Does an O3 app or module resolve this by uuid, code or name, such that an implementation changing it breaks the feature rather than customising it?

Not "is it needed to run O3" — as @ibacher points out, most of the demo package is strictly necessary too, so that question discriminates nothing. Awkwardly, the README sentence my first pass quoted states the right test; I quoted it and then applied a different one.

What moves here

why
encounter types, encounter roles O3 apps and the emrapi mappings resolve these by uuid
Privilege Level: Full/High, the Application: … roles, module privileges O3 checks these by name
emrapi metadata source + the fixed term mappings emr.admissionEncounterType, emr.visitNoteEncounterType, emr.clinicianEncounterRole, …
dispositions + the ADT concepts emrapi/ward resolve them by concept code (CIEL:168619 lives only in IPD-DEMO, which comes along)
the vital sign concepts + Vital signs set esm-patient-vitals-app resolves them by uuid
CDU, CLF, CIELAllergySet, DeathCause, DrugDispense, StockManagementConcepts, VitalSignCodes the dictionaries the above resolve against

What stays in the demo package after the rework

Everything an implementation may reasonably want its own version of, even where O3 needs something there: the identifier types with their idgen source and autogeneration option (sites use their own schemes), visit types, order frequencies, relationship and person attribute types, visit attribute types, procedure types, the vitals/BMI reference ranges (clinical policy), print and ID-sticker defaults, misc concepts, metadata sets, attachments.allowedFileExtensions — plus locations, programs, forms, queues, appointment services and all the catalogs, as before.

metadatatermmappings is split, not moved: emr.primaryIdentifierType and emr.extraPatientIdentifierTypes follow the identifier types they point at, because the mapping must exist but its value is the implementation's choice. That also keeps the invariant that this package references nothing defined only in the demo package — re-verified by sweeping every uuid and CIEL code after the rework.

The one thing I'm knowingly stretching

The Unknown Location tagging. Locations are the canonical implementation-owned thing, so strictly it doesn't belong here. But O3's login page only offers locations tagged Login Location, and with the demo package left out there is nothing to log in to — so this tags the one location that isn't a site's choice, the Unknown Location openmrs-core creates in every database. No facility invented, no uuid created.

Reject this bit if you'd rather. I think it points at a real gap: a bootstrap package (one placeholder location, one identifier source, one visit type) would be the honest home for it, and would let no-demo boot without parking modifiable content here. Evidence it's a real gap and not hypothetical: openmrs/openmrs-distro-referenceapplication@b60dce9 added a no-demo profile last December, and I built and booted it — base 1.4.0 has no location tagged Login Location, so that image cannot complete login and nobody had noticed.

Separately and regardless of how this lands: with no location tagged Queue Location, /home resolves to the Service Queues dashboard and @openmrs/esm-service-queues-app 11.1.0 throws Cannot read properties of undefined (reading 'id') — the first screen after login is an error page. Reproduced, and fixed by the tag.

Verification

  • Nothing lost: 100 config files across the two packages before, 103 after, the three extra being the intentional splits (Vital signs, the two core global properties, the tagged Unknown Location). Row/property unions checked file by file.

  • Both packages build and pass validate-content-package.

  • Booted this package on its own (no demo package, no referencedemodata) and drove the O3 login in a browser. Login completes on Unknown Location, and all eight routes render without errors — /home, appointments, wards, patient lists, laboratory, registration, patient search, system administration.

  • But base-alone is not a usable EMR, and this PR does not claim otherwise. Measured on that same instance: visittype is empty, idgen/identifiersource is empty, and the only identifier types are core's own two. So you cannot start a visit, and O3's registration has no source to generate a primary identifier from — a patient can only be created by supplying an identifier by hand through the API. Concepts total 378.

    That is the correct outcome of the boundary, not a regression: visit types and identifier schemes are things a site defines. It is also precisely the argument for the bootstrap package below — an earlier revision of this PR made base-alone fully usable, but only by parking implementation-modifiable metadata here, which is the mistake @ibacher caught.

  • Regenerating a demo database from the pair still gives 50 patients, all with encounters, 5821 obs, 278 visits, 11 locations, 7 forms, 4 programs, 0 Site N, and Privilege Level: Full grants identical to the demo-free database (308 = 308).

Notes for reviewers

Filenames are otherwise unchanged on purpose, so several files here are still named *-core_demo.csv. @ibacher suggests fixing the naming rather than the placement — I agree the naming misled me, and I'd happily do a rename pass, but as a separate PR so a 28-file move isn't buried in it.

Existing installs re-import the moved files once: build-distro lays config out as openmrs_config/<domain>/<package>/<file> and names checksums <domain>/<package>_<file>.checksum, so changing the owning package changes both. Idempotent — declarative CSV/XML keyed by uuid.

Merge before openmrs-content-referenceapplication-demo#81.

No O3- ticket yet — happy to retitle.

🤖 Generated with Claude Code

https://claude.ai/code/session_015Rb27z8QxDsmWEkt7Cs3s7

dkayiwa and others added 2 commits August 12, 2026 12:22
The two content packages were split by name rather than by role.
`referenceapplication` was a 10-file overlay - concept classes and sources, the
location tags, privileges, four global properties, two OCL packages - while
`referenceapplication-demo` carried everything needed to actually run O3: the
`OpenMRS ID` identifier type and its idgen source, encounter and visit types,
the `Privilege Level` and `Application: ...` roles, the emrapi term mappings,
ADT dispositions, order frequencies, dosing units, and the vital sign concepts
with their reference ranges. So a distribution could not drop the demo package -
the point of the `no-demo` profile in openmrs-distro-referenceapplication@b60dce9
- without losing the ability to log in, register a patient or record an obs.

Filenames are unchanged, so the config is identical file for file: every moved
file is byte-identical and no name collides. Three files held both kinds of
content and were split: `OpenMRS ID` out of the identifier types, `Vital signs`
out of the convsets (`Tests Orderability` stays with the lab catalog it names),
and queue.sortWeightGenerator / concept.causeOfDeath /
attachments.allowedFileExtensions out of the demo global properties.

Existing installations do re-import the moved files once. build-distro lays
config out as openmrs_config/<domain>/<package>/<file> and names checksums
<domain>/<package>_<file>.checksum, so changing which package owns a file
changes both its path and its checksum name. Re-import is idempotent - these are
declarative CSV and XML keyed by uuid - but it also shifts their order within a
domain, since `referenceapplication` sorts ahead of `referenceapplication-demo`.

The boundary the README already describes decided each case: what an O3 feature
resolves by uuid, code or name belongs here; what a site supplies or replaces
stays in the demo package. Locations stay there, so this package defines no
facility of its own. Catalogs stay too, which is why alpreferenceranges.csv and
hbreferenceranges.csv did not come along: their Hb and ALP concepts live in
`BasicLabTests`. dispositionConfig.json did come, and brought the misleadingly
named `IPD-DEMO` OCL package with it, since that is where its admit concept is.

The service queues frontend config goes the other way, to the demo package: its
priority and status uuids are `DemoQueueConcepts`, and it was this package's
last dependency on the demo package. There are now none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Rb27z8QxDsmWEkt7Cs3s7
…and used

O3's login page only offers locations tagged `Login Location`, and this package
defines no locations - deliberately, since locations vary by site. That left a
distribution built without the demo package unable to finish logging in.

openmrs-core already creates `Unknown Location` in every database, and the demo
package carried a row for it that restated core's own name and description and
tagged nothing - a no-op. Move that row here and tag it `Login Location`,
`Visit Location` and `Queue Location`. Nothing is invented: no placeholder
facility name, no new uuid, and the set of locations across the two packages is
unchanged.

`Queue Location` is there because `/home` resolves to the Service Queues
dashboard: with no location carrying that tag, `@openmrs/esm-service-queues-app`
11.1.0 throws `Cannot read properties of undefined (reading 'id')` and the first
screen after login is an error page. Verified by driving the login of a packaged
standalone built from this package alone - the dashboard renders its stat tiles
and empty queue table once the tag is present. `Admission Location` and
`Transfer Location` are deliberately left off: admitting a patient to "Unknown
Location" is meaningless, so a site tags its own wards.

It is a placeholder, not a facility, and the README says so: sites add their own
locations, tag those, and retire this one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Rb27z8QxDsmWEkt7Cs3s7
Review feedback (#13): the division between the two packages is not "needed to
run O3" - most of the demo package's metadata is strictly necessary too - but
"what an implementation may want its own version of" versus "what must be set
this way". The previous commits used the wrong test, which is doubly awkward
because the README sentence they quote states the right one.

Applying the right test, these go back to the demo package. Each is something a
site legitimately defines for itself, even though O3 needs *something* there:

  identifier types + the idgen source and autogeneration option (sites use their
  own identifier schemes), visit types (local practice), order frequencies (local
  prescribing), relationship types, person attribute types, visit attribute types,
  procedure types, the vitals/BMI reference ranges (clinical policy), the
  encounter-print and ID-sticker defaults, misc concepts, metadata sets, and
  attachments.allowedFileExtensions.

`metadatatermmappings` is split rather than moved: the mappings whose targets are
fixed (`emr.admissionEncounterType`, `emr.visitNoteEncounterType`,
`emr.clinicianEncounterRole`, ...) stay here, while `emr.primaryIdentifierType`
and `emr.extraPatientIdentifierTypes` follow the identifier types they point at -
the mapping must exist, but its value is the implementation's choice. That also
preserves the invariant that this package references nothing defined only in the
demo package; re-verified by sweeping every uuid and CIEL code.

Renamed the remaining file to metadatatermmappings-core.csv so the two packages
do not both ship a file called metadatatermmappings-core_demo.csv.

What stays: encounter types and roles, the roles/privileges O3 checks by name, the
emrapi metadata source and its fixed mappings, dispositions and the ADT concepts,
the vital sign concepts and the `Vital signs` set, and the OCL packages those
resolve against.

The `Unknown Location` tagging stays too, flagged in the README as a bootstrap
exception rather than content - without it a distro built without the demo package
has nothing to log in to. It arguably belongs in a separate minimal package with
an identifier source and a visit type; that is a design question for the split,
not something to settle by quietly parking modifiable content here.

Nothing is lost across the two packages: 100 config files before, 103 after, the
three extra being the intentional splits (Vital signs, the two core global
properties, the tagged Unknown Location). Both packages build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Rb27z8QxDsmWEkt7Cs3s7
dkayiwa added a commit to openmrs/openmrs-content-referenceapplication-demo that referenced this pull request Aug 12, 2026
Follow-up to review feedback on
openmrs/openmrs-content-referenceapplication#13: the division between the two
packages is "what an implementation may want its own version of" versus "what
must be set this way", not "needed to run O3". The first commit used the wrong
test and moved out several things a site legitimately owns.

These stay here after all: the identifier types with their idgen source and
autogeneration option, visit types, order frequencies, relationship types, person
attribute types, visit attribute types, procedure types, the vitals/BMI reference
ranges, the encounter-print and ID-sticker defaults, the misc concepts, the
metadata sets, and attachments.allowedFileExtensions.

`metadatatermmappings` keeps `emr.primaryIdentifierType` and
`emr.extraPatientIdentifierTypes`, which follow the identifier types they point
at: the mappings must exist, but their values are the implementation's choice. The
fixed encounter and role mappings stay in the base package.

Still moved out, because an implementation changing them breaks the feature rather
than customising it: encounter types and roles, the roles and privileges O3 checks
by name, the emrapi metadata source, dispositions and their ADT concepts, the vital
sign concepts and the `Vital signs` set, and the OCL packages those resolve
against.

Nothing is lost across the two packages - 100 config files before, 103 after, the
extra three being intentional splits. Regenerating a demo database from this pair
still yields 50 patients, all with encounters, 5821 obs, 278 visits, 11 locations,
7 forms, 4 programs and no `Site N`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Rb27z8QxDsmWEkt7Cs3s7
@dkayiwa dkayiwa changed the title Take the metadata O3 requires out of the demo content package Move the metadata that must not be changed into the base content package Aug 12, 2026
Two corrections to the README, both found by booting a distribution built from
this package alone and measuring rather than reasoning:

  - It now says outright that logging in is as far as this package gets you.
    Login completes and every O3 route renders, but `visittype` and
    `idgen/identifiersource` are both empty, so no visit can be started and O3's
    registration has nothing to generate a primary identifier from. Both are
    site-defined, so they belong in the demo package - but a reader should not
    have to discover that by booting it.

  - Removed a paragraph left duplicated by the previous edit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Rb27z8QxDsmWEkt7Cs3s7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant