This case study scores a real, public ADaM package end to end. Nothing is hand-tuned: we take two artifacts that were produced independently and ask how ready the result is for submission.

  • The specification comes from metacore: the CDISC pilot ADaM metadata, describing what each dataset and variable should be.
  • The datasets come from pharmaverseadam: ADaM data built with admiral.

Because the spec and the data were not made to match each other, the score is not a formality. It tells us where the package stands.

The inputs

library(r4subpharma)

# The CDISC pilot ADaM specification (covers ADSL, ADAE, ADLBC, ADADAS, ADTTE)
e <- new.env()
load(metacore::metacore_example("pilot_ADaM.rda"), envir = e)
spec <- e$metacore

# One of the admiral-built datasets
adsl <- pharmaverseadam::adsl
dim(adsl)
#> [1] 306  55

One call to a score

submission_readiness() harvests documentation evidence from the spec and conformance evidence from the dataset, then computes the Submission Confidence Index.

ctx <- r4subcore::r4sub_run_context("CDISCPILOT01", "PROD")
#>  Run context created: "R4S-20260904111030-wl4dieex"
spec_adsl <- metacore::select_dataset(spec, "ADSL", verbose = "silent")

res <- submission_readiness(list(ADSL = adsl), spec_adsl, ctx)
#>  metacore_to_evidence: 102 rows from 51 variables
#>  Evidence table created: 102 rows
#>  adam_to_evidence: 125 rows for dataset "ADSL"
#>  Evidence table created: 125 rows
#>  Bound 2 evidence tables: 227 total rows
#>  Submission Confidence Index: 79.5 (minor_gaps)
res
#> <submission_readiness>
#>   evidence rows: 227 
#>   SCI:           79.5 
#>   band:          minor_gaps

Reading the score

The pillar breakdown shows where the number comes from.

as.data.frame(res$sci$pillar_scores)
#>      pillar pillar_score n_indicators weight
#> 1   quality    0.9934641            3   0.35
#> 2     trace    0.3933824            2   0.25
#> 3      risk           NA            0   0.25
#> 4 usability    1.0000000            1   0.15

Quality (documentation and types) and usability (labels) are strong. The weak pillar is traceability: the specification lists variables that the built dataset does not yet contain. That single gap is what holds the score back.

The gap, made actionable

The value of a score is the fix list behind it. The failing traceability checks name exactly which specified variables are missing from the data.

ev <- res$evidence
missing <- ev[ev$indicator_id == "T-ADAM-001" & ev$result == "fail", ]
nrow(missing)
#> [1] 30
sub(".*: ", "", missing$message)
#>  [1] "Variable SITEGR1 is described in metadata but missing from ADSL" 
#>  [2] "Variable TRT01PN is described in metadata but missing from ADSL" 
#>  [3] "Variable TRT01AN is described in metadata but missing from ADSL" 
#>  [4] "Variable AVGDD is described in metadata but missing from ADSL"   
#>  [5] "Variable CUMDOSE is described in metadata but missing from ADSL" 
#>  [6] "Variable AGEGR1N is described in metadata but missing from ADSL" 
#>  [7] "Variable AGEGR2 is described in metadata but missing from ADSL"  
#>  [8] "Variable AGEGR2N is described in metadata but missing from ADSL" 
#>  [9] "Variable RACEN is described in metadata but missing from ADSL"   
#> [10] "Variable ITTFL is described in metadata but missing from ADSL"   
#> [11] "Variable EFFFL is described in metadata but missing from ADSL"   
#> [12] "Variable COMP8FL is described in metadata but missing from ADSL" 
#> [13] "Variable COMP16FL is described in metadata but missing from ADSL"
#> [14] "Variable COMP24FL is described in metadata but missing from ADSL"
#> [15] "Variable DISCONFL is described in metadata but missing from ADSL"
#> [16] "Variable DSRAEFL is described in metadata but missing from ADSL" 
#> [17] "Variable BMIBL is described in metadata but missing from ADSL"   
#> [18] "Variable BMIBLGR1 is described in metadata but missing from ADSL"
#> [19] "Variable HEIGHTBL is described in metadata but missing from ADSL"
#> [20] "Variable WEIGHTBL is described in metadata but missing from ADSL"
#> [21] "Variable EDUCLVL is described in metadata but missing from ADSL" 
#> [22] "Variable DISONSDT is described in metadata but missing from ADSL"
#> [23] "Variable DURDIS is described in metadata but missing from ADSL"  
#> [24] "Variable DURDSGR1 is described in metadata but missing from ADSL"
#> [25] "Variable VISIT1DT is described in metadata but missing from ADSL"
#> [26] "Variable VISNUMEN is described in metadata but missing from ADSL"
#> [27] "Variable RFENDT is described in metadata but missing from ADSL"  
#> [28] "Variable DCDECOD is described in metadata but missing from ADSL" 
#> [29] "Variable DCSREAS is described in metadata but missing from ADSL" 
#> [30] "Variable MMSETOT is described in metadata but missing from ADSL"

These are the CDISC pilot analysis variables: treatment-coded flags, age and site groupings, and completion flags. An analysis-ready ADSL is expected to carry them, so their absence is a real readiness finding, not a formatting nit.

We can also see the smaller quality signals: any variable whose type does not match the specification.

ev[ev$indicator_id == "Q-ADAM-001" & ev$result == "warn",
   c("location", "message")]
#> [1] location message 
#> <0 rows> (or 0-length row.names)

Scoring more of the package

The same call scales to several datasets at once; the score then reflects the whole set of evidence.

res_pkg <- submission_readiness(
  list(ADSL = pharmaverseadam::adsl, ADAE = pharmaverseadam::adae),
  spec,
  ctx
)
#>  metacore_to_evidence: 436 rows from 218 variables
#>  Evidence table created: 436 rows
#>  adam_to_evidence: 125 rows for dataset "ADSL"
#>  Evidence table created: 125 rows
#>  adam_to_evidence: 201 rows for dataset "ADAE"
#>  Evidence table created: 201 rows
#>  Bound 3 evidence tables: 762 total rows
#>  Submission Confidence Index: 82.8 (minor_gaps)
res_pkg$sci$SCI
#> [1] 82.8
as.data.frame(res_pkg$sci$pillar_scores)
#>      pillar pillar_score n_indicators weight
#> 1   quality    0.9954128            3   0.35
#> 2     trace    0.4893868            2   0.25
#> 3      risk           NA            0   0.25
#> 4 usability    1.0000000            1   0.15

Takeaways

  • The score is honest. On genuinely independent inputs it lands in the “minor gaps” band, driven down by a specific, explainable traceability gap.
  • The evidence is actionable: the failing checks are a prioritized list of variables to add, not an opaque number.
  • It is reproducible on public data, and the evidence table flows into the rest of the R4SUB ecosystem. Use r4subprofile for authority-specific weighting and r4subrisk for risk quantification, with no extra work. ```