Scoring submission readiness from a pharmaverse pipeline

r4subpharma bridges a pharmaverse pipeline and the R4SUB ecosystem. It reads the metadata and datasets you already build and emits standardized evidence that r4subscore can turn into a Submission Confidence Index (SCI). Nothing about your pipeline has to change: you add one block at the end.

library(r4subpharma)

The metadata contract

Both adapters operate on one small table with a row per dataset variable. You can hand it a data.frame directly, or a metacore object, which as_variable_metadata() unpacks for you.

meta <- data.frame(
  dataset  = "ADSL",
  variable = c("USUBJID", "AGE", "SEX", "TRTSDT"),
  label    = c("Unique Subject Identifier", "Age", "Sex", "Date of First Exposure"),
  type     = c("text", "integer", "text", "integer"),
  origin   = c("Predecessor", "Derived", "Predecessor", "Derived"),
  derivation = c(NA, "Age at informed consent", NA, "First dosing date from EX"),
  stringsAsFactors = FALSE
)

as_variable_metadata(meta)
#> # A tibble: 4 × 7
#>   dataset variable label                     type   origin derivation is_derived
#>   <chr>   <chr>    <chr>                     <chr>  <chr>  <chr>      <lgl>     
#> 1 ADSL    USUBJID  Unique Subject Identifier text   Prede… <NA>       FALSE     
#> 2 ADSL    AGE      Age                       integ… Deriv… Age at in… TRUE      
#> 3 ADSL    SEX      Sex                       text   Prede… <NA>       FALSE     
#> 4 ADSL    TRTSDT   Date of First Exposure    integ… Deriv… First dos… TRUE

With a real metacore object the call is identical — this is how you would wire it into an existing spec:

mc <- metacore::spec_to_metacore("adam_spec.xlsx")
meta <- as_variable_metadata(metacore::select_dataset(mc, "ADSL"))

Evidence from metadata

metacore_to_evidence() scores how completely each variable is documented, reusing the Q-DEFINE-002 (documented) and Q-DEFINE-003 (derivation present) indicators so this evidence lines up with anything parsed straight from Define-XML.

ctx <- r4subcore::r4sub_run_context("STUDY01", "PROD")
#> ℹ Run context created: "R4S-20260831223709-xmf6oiv3"
ev_meta <- metacore_to_evidence(meta, ctx)
#> ℹ metacore_to_evidence: 6 rows from 4 variables
#> ✔ Evidence table created: 6 rows

ev_meta[, c("indicator_id", "location", "result", "severity")]
#>   indicator_id     location result severity
#> 1 Q-DEFINE-002 ADSL:USUBJID   pass     info
#> 2 Q-DEFINE-002     ADSL:AGE   pass     info
#> 3 Q-DEFINE-002     ADSL:SEX   pass     info
#> 4 Q-DEFINE-002  ADSL:TRTSDT   pass     info
#> 5 Q-DEFINE-003     ADSL:AGE   pass     info
#> 6 Q-DEFINE-003  ADSL:TRTSDT   pass     info

Evidence from an ADaM dataset

adam_to_evidence() compares a built dataset against the same metadata. Here SEX is missing, STUDYID is undescribed, and no labels have been applied yet — each becomes an evidence row across the trace, quality, and usability pillars.

adsl <- data.frame(
  USUBJID = c("01-001", "01-002"),
  AGE     = c(54, 61),
  TRTSDT  = c(19100, 19112),
  STUDYID = c("STUDY01", "STUDY01"),
  stringsAsFactors = FALSE
)

ev_adam <- adam_to_evidence(adsl, meta, ctx, dataset_name = "ADSL")
#> ℹ adam_to_evidence: 11 rows for dataset "ADSL"
#> ✔ Evidence table created: 11 rows

ev_adam[, c("indicator_id", "indicator_domain", "location", "result")]
#>    indicator_id indicator_domain     location result
#> 1    T-ADAM-001            trace ADSL:USUBJID   pass
#> 2    T-ADAM-001            trace     ADSL:AGE   pass
#> 3    T-ADAM-001            trace     ADSL:SEX   fail
#> 4    T-ADAM-001            trace  ADSL:TRTSDT   pass
#> 5    T-ADAM-002            trace ADSL:STUDYID   warn
#> 6    Q-ADAM-001          quality ADSL:USUBJID   pass
#> 7    Q-ADAM-001          quality     ADSL:AGE   pass
#> 8    Q-ADAM-001          quality  ADSL:TRTSDT   pass
#> 9    Q-ADAM-002        usability ADSL:USUBJID   fail
#> 10   Q-ADAM-002        usability     ADSL:AGE   fail
#> 11   Q-ADAM-002        usability  ADSL:TRTSDT   fail

One call to a score

submission_readiness() runs both adapters over a set of datasets and, when r4subscore is installed, computes the SCI.

res <- submission_readiness(list(ADSL = adsl), meta, ctx)
#> ℹ metacore_to_evidence: 6 rows from 4 variables
#> ✔ Evidence table created: 6 rows
#> ℹ adam_to_evidence: 11 rows for dataset "ADSL"
#> ✔ Evidence table created: 11 rows
#> ✔ Bound 2 evidence tables: 17 total rows
#> ℹ Submission Confidence Index: 65.4 (conditional)
res
#> <submission_readiness>
#>   evidence rows: 17 
#>   SCI:           65.4 
#>   band:          conditional
res$sci$SCI
#> [1] 65.4
res$sci$band
#> [1] "conditional"

How the pieces map to the SCI

Source Indicators Pillar
metacore_to_evidence() Q-DEFINE-002, Q-DEFINE-003 quality
adam_to_evidence() T-ADAM-001, T-ADAM-002 trace
adam_to_evidence() Q-ADAM-001 quality
adam_to_evidence() Q-ADAM-002 usability

Because the adapters emit the standard R4SUB evidence schema, the resulting table also flows into r4subrisk for risk quantification and r4subprofile for authority-specific weighting, exactly like evidence from any other source.