open.gismo · convention over configuration · page 2 of 2

What was measured, and how

Every claim on the other two pages traces to something here: a contract read out of the workflow YAMLs, a matcher run against a hand-authored delivery, a dependency graph traced through the metric workflows' own specs, and a survey slice read from public documentation. The failures were executed rather than reasoned about.

1 · The contract

Read on 2026-08-27 from demo-301/workflows/1_mappings/*.yaml — the study configuration that carries both the RBQM lens and the Safety charts, which is the realistic target for “bring your own study.” Every Raw_* domain in every spec: block, sentinel keys (names beginning _) dropped, merged across mappings.

14
raw domains
126
distinct required columns
30
metric workflows, of which 1 declares no spec
47
distinct columns any metric names

og_init()'s default snapshot is smaller — 13 mapping workflows, 12 raw domains, 90 columns — because it omits ECG, inclusion/exclusion and the safety-chart mappings. Both numbers are correct; they describe different studies.

Two properties of the contract that shape any design:

2 · The delivery, and the matcher run

Seven files, 105 source columns, authored from the CDISC ADaM IG and SDTMIG variable lists plus EDC-vendor export conventions — written before the alias table, so the matcher gets no help from having been fitted to it. Both were written by the same author, which is the honest limitation of this measurement and is stated again at the bottom.

Tier 0 — no aliases at all

Exact case-insensitive name matching across the whole delivery settles 44 of 126. That is higher than the “a conformant CDISC package matches zero” framing suggests, and the reason matters: most of the 44 are studyid, subjid, country, sex, race, arm repeated across domains, plus the EDC vendor's City/State/Country, which are not CDISC at all.

✖ The most confident tier produced the worst error

Raw_SUBJ.subjid ← SUBJID is one of those 44. In ADaM, SUBJID is the site-level subject number and USUBJID is the study-unique one; adae and adlb carry only USUBJID, so those domains resolve subjid to a different identifier. Case-insensitive exact matching is the highest-confidence rule available and here it is the one that breaks the study. Any design that ranks matches by confidence will rank this one first.

Tiers 1 and 2 — the alias table

73
of 126 settled with no human input (58%)
22
of the 34 metric-relevant columns settled
12
metric-relevant columns left open
11
settled by aliases lifted from safetyCharts
DomainsettledofLeft open
Raw_ENROLL77✔ complete
Raw_QUERY55✔ complete
Raw_SITE99✔ complete
Raw_AE1416aeongo, mincreated_dts
Raw_LB1114lbstnrlo, lbstnrhi, toxgrg_nsv
Raw_SUBJ1115subject_nsv, timeonstudy, firstparticipantdate, mincreated_dts
Raw_PD56deemedimportant
Raw_STUDCOMP56mincreated_dts
Raw_DATACHG34n_changes
Raw_DATAENT34data_entry_lag
Raw_EG010✖ nothing delivered
Raw_IE010✖ nothing delivered
Raw_SDRGCOMP05✖ nothing delivered
Raw_STUDY015✖ no CTMS extract

Three domains complete themselves entirely, and the two biggest holes are domains nobody sent rather than columns nobody matched. That shape is what makes “decline it, and here is what it costs” the right primitive: a third of the outstanding work is not mapping at all, it is a study telling the product what it does not have.

How the 73 were settledcolumnsReliability
Exact, case-insensitive name match27⚠ includes the error above
EDC-vendor export conventions (hand)16✔ strong within a vendor
CDISC IG variable index (hand)13✔ strong
safetyCharts::meta_aes6✔ per-standard defaults
safetyCharts::meta_labs4✔ per-standard defaults
Semantically near, not equal (hand, flagged weak)6⚠ must be reviewed
safetyCharts::meta_dm1✔ per-standard defaults

The six flagged weak are the interesting ones and they are why a matcher must be able to say “near”: Raw_SUBJ.enrollyn ← ITTFL, Raw_ENROLL.enroll_dt ← RANDDT, Raw_STUDCOMP.compyn ← COMPLFL, Raw_STUDCOMP.compreas ← DCSREAS, Raw_LB.battrnam ← PARCAT1. Each is defensible and each is a study-specific judgement — an intent-to-treat flag is not an enrolment flag, and randomisation is not enrolment. These are exactly the lines a reviewer needs to see in a diff, and exactly the ones a wizard would let a user click past.

3 · Pricing — what each gap actually costs

Traced through the metric workflows' own spec: blocks in demo-301/workflows/2_metrics/: 30 workflows, 136 spec entries, 47 distinct Mapped_* columns. The dependency is heavily concentrated.

columnmetric workflows that name it
Mapped_SUBJ.subjid24
Mapped_SUBJ.invid12
Mapped_SUBJ.country11
Mapped_SUBJ.timeonstudy8
Mapped_AE.subjid5
Mapped_PD.subjid4
Mapped_PD.deemedimportant4
Mapped_QUERY.querystatus4
Mapped_LB.subjid3
Mapped_AE.aeser3

Only 34 of the 126 required columns are named by any metric at all. The other 92 feed reporting, labels and the study masthead. That ratio is the load-bearing fact under this whole direction: the mapping job a user must actually get right is a quarter of the size the contract implies, and a diagnostic that orders findings by cost turns a 53-line chore into a 12-line one. Get the ordering wrong and the direction fails on volume — that is W4 on the design page.

The two executed failures

failure 1 · silent

Empty join

Mapped_LB and Mapped_AE arrive with zero rows. Metrics that go to zero at every site: cou0001 cou0002 cou0005 kri0001 kri0002 kri0005 saf0001 saf0003 — 8 of 29. Metrics that still report normally: 21. No error, no warning, no missing column.

srs0001 declares no spec at all, so it weights whichever kri* workflows ran. A partial mapping therefore changes the site risk score's denominator instead of failing — the silent-divisor defect, arriving through the data-loading door.

failure 2 · loud but unpriced

Unobtainable column

Raw_LB.toxgrg_nsv has no source in SDTM or ADaM. It costs exactly cou0005 and kri0005, and one input to srs0001. Today the user is told “missing column: toxgrg_nsv” and is given no way to tell whether that is fatal or cosmetic, and nowhere to say so if it is permanent.

4 · The asset that already exists — and its first bug

safetyCharts ships nine metadata tables, 82 rows, each row carrying standard_adam and standard_sdtm defaults. It is MIT-licensed and @jwildfire is its author and maintainer. It contains two kinds of row, and the second kind is the more interesting one:

typerowsWhat it mapsgsm.mapping equivalent
column68A chart role to a standard's variable name — value_colAVAL / LBSTRESNsource_col exists; the defaults do not
field14A chart's literal value to a standard's value — measure_values--ALT"Alanine Aminotransferase (U/L)" in ADaM, "Alanine Aminotransferase" in SDTM✖ no seam at all

The 14 field rows are a value-level metadata layer that safetyGraphics has had for years and that gsm has nowhere — which is why hep_explorer.yaml ends up carrying four bare literal strings in lSettings.measure_values that a study must happen to match. The ADaM defaults in those rows even carry the units inside the string, "Alanine Aminotransferase (U/L)", which is exactly the unit problem showing through the vocabulary.

Bridging chart roles to gsm columns takes a hand-written table of 29 pairs. Doing that yields 52 alias rows covering 26 gsm columns for free.

⚠ And the seed's first defect, found by running it

meta_labs gives the ADaM reference range as A1LO / A1HI. The delivery used ANRLO / ANRHI. Both are legitimate ADaM spellings for an analysis reference range, so the alias missed a column it should have caught, and Raw_LB.lbstnrlo / lbstnrhi landed in the unsettled pile — with lbstnrhi being one of the 34 columns a metric names.

The lesson is a design requirement, not a complaint: aliases must be many-to-one, with several accepted spellings per standard, and the table needs a regression test per standard rather than a one-time import. That is decision C6.

5 · The landscape, read through one question

The thirteen-platform survey is not repeated here — the gap analysis holds it and the sibling artifact re-visited it for the screen-based direction. This slice asks one question of the same sources: when a platform's mapping is finished, is there a file, and does the study own it?

PlatformHow a user's data gets inMapping is a file?In the study's own VCS?
teal / teal.modules.clinicalYou hand it ADaM. There is no mapping surface — the mapping is your ADaM programming.✔ your ADaM code✔ yes
clinDataReviewYAML configuration per report, run from the console.✔ YAML✔ yes
DaVinci dv.*Modules take pre-shaped R data frames; shaping is your code.✔ your code✔ yes
safetyGraphicsBoth: detectStandard() pre-fills an interactive mapping tab, and safetyGraphicsApp() also accepts a mapping argument programmatically.⚠ if you use the R path⚠ if you save it
tidyCDISCUpload; a missing required variable is rejected. No mapping surface.✖ none✖ n/a
JMP ClinicalPoints at SDTM / ADaM libraries with a domain-detection step on load.⚠ project state✖ no
Medidata Clinical Data StudioPlatform-side ingestion and standardisation, configured in the product.✖ platform state✖ no
Veeva CDBPlatform-side ingestion; conversion to a canonical model is the product's job.✖ platform state✖ no
elluminateA data-mapping surface inside the platform, typically run with vendor services.✖ platform state✖ no
CluePointsIngestion and mapping delivered as part of the engagement.✖ vendor-side✖ no
Oracle Empirica SignalConfigured data ingestion into the platform's own model.✖ platform state✖ no

Documented, not verified: each row reflects what the platform's public documentation, package reference index or user guide describes. Nothing was tested, no demo requested, no vendor contacted, and vendor documentation flatters. Where a row is a judgement about the shape of the product rather than a documented feature, it is written as such.

Three readings of that table

6 · Method, and what would change these numbers

What would move the 73

Both halves were written by the same author. The delivery came from the CDISC variable lists and the alias table came from safetyCharts plus the same lists, so a shared assumption inflates the score in a way a real CRO delivery would not. The number to trust is the shape, not the value: name matching alone gets roughly a third, a standards-aware alias table roughly another quarter, and the residue is dominated by domains nobody sent rather than by columns nobody matched. A second delivery from a different EDC vendor would move the vendor-convention 16 more than anything else. Running this against a real, non-synthetic delivery is the first thing that should happen if this direction is picked.