Review ask

As a clinical trial statistician or programmer, challenge anything in this benchmark: describe a problem in Comments, or propose a fix with the Edit button on any source file.

  1. Task: Would a real study need this derivation, specified this way? Does it follow CDISC ADaM: variable names, controlled terms, record structure? Flag any rule that is ambiguous or that a sponsor would handle differently.
  2. Inputs: Do they look like data collected in a real trial, and do they include the situations you would expect to meet in one?
  3. Expected output: Would independent QC programming reproduce every value from these inputs and rules?

The other tabs beside Comments are background for assessment, not under review.

Summary

Lifecycle: reviewedEditUnder review
What this benchmark means
README.md

Goal: derive one overall-survival record per subject with parameter code OS (Overall Survival), carrying STARTDT, ADT, AVAL, CNSR, EVNTDESC, CNSDTDSC, SRCDOM, SRCVAR, and SRCSEQ.

Input: subject-level randomization and last-known-alive dates (RANDDT, LSTALVDT) alongside response records holding death analysis dates (ADT). The death selection keeps only dated response records where PARAMCD is DEATH, AVALC is Y, and ANL01FL is Y; it takes the one with the earliest ADT (on a tied date, the lower sequence number wins) and treats a subject with no such record as having no death.

Variables:

  • STARTDT is the randomization date (RANDDT), the origin from which survival time is counted.
  • ADT is the analysis date: the death date when a qualifying death falls on or after STARTDT, and STARTDT itself when the death falls before it. With no death, ADT is the last-alive date when that date falls after randomization, else the randomization date (including a tie).
  • AVAL is the number of days from STARTDT through ADT, counting both endpoints.
  • CNSR is 0 when the subject has a qualifying death and 1 otherwise.
  • EVNTDESC is Death when the subject has a qualifying death, Alive when censoring at a last-alive date after randomization, and Randomization otherwise.
  • CNSDTDSC is blank for a death, Alive During Study when censoring at the last-alive date, and Randomization when censoring at randomization.
  • SRCDOM is ADRS for a death and ADSL for censoring.
  • SRCVAR is ADT for a death, LSTALVDT when censoring at the last-alive date, and RANDDT otherwise.
  • SRCSEQ is the sequence number from the selected death record, and blank when the record is censored.

Note: a death dated before randomization still counts as an event: ADT is set to randomization while CNSR, EVNTDESC, and the source columns still reflect the death.

Inputs

Under review
2 source files

input/adrs.csv

Edit
10 rows
input/adrs.csv
STUDYIDUSUBJIDPARAMCDAVALCANL01FLADTASEQ
YAMAA-01DEATHYY2026-06-011
YAMAA-01DEATHNY2026-06-011
YAMAA-01DEATHYY2026-04-012
YAMAA-01DEATHYY2026-02-011
YAMAA-01DEATHYY2026-07-013
YAMAA-01DEATHYY2026-07-012
YAMAA-01BORYY2026-02-013
YAMAA-01DEATHYN2026-03-011
YAMAA-01DEATHYY2026-05-012
YAMAA-01DEATHYY—1

input/adsl.csv

Edit
10 rows
input/adsl.csv
STUDYIDUSUBJIDRANDDTLSTALVDT
YAMAA-012026-01-01—
YAMAA-012026-01-012026-09-01
YAMAA-012026-01-01—
YAMAA-012026-03-012026-02-01
YAMAA-012026-04-012026-04-01
YAMAA-012026-05-01—
YAMAA-012026-02-01—
YAMAA-012026-01-012026-12-01
YAMAA-012026-01-01—
YAMAA-012026-01-012026-12-01

Expected output

Under review
10 expected rows

expected/adtte.csv

Edit
10 rows
expected/adtte.csv
STUDYIDUSUBJIDPARAMCDPARAMSTARTDTADTAVALCNSREVNTDESCCNSDTDSCSRCDOMSRCVARSRCSEQ
YAMAA-01OSOverall Survival2026-01-012026-06-011520Death—ADRSADT1
YAMAA-01OSOverall Survival2026-01-012026-09-012441AliveAlive During StudyADSLLSTALVDT—
YAMAA-01OSOverall Survival2026-01-012026-01-0111RandomizationRandomizationADSLRANDDT—
YAMAA-01OSOverall Survival2026-03-012026-03-0111RandomizationRandomizationADSLRANDDT—
YAMAA-01OSOverall Survival2026-04-012026-04-0111RandomizationRandomizationADSLRANDDT—
YAMAA-01OSOverall Survival2026-05-012026-05-0110Death—ADRSADT2
YAMAA-01OSOverall Survival2026-02-012026-02-0110Death—ADRSADT1
YAMAA-01OSOverall Survival2026-01-012026-07-011820Death—ADRSADT2
YAMAA-01OSOverall Survival2026-01-012026-05-011210Death—ADRSADT2
YAMAA-01OSOverall Survival2026-01-012026-12-013351AliveAlive During StudyADSLLSTALVDT—
Shaded columns are derived— = missing value

Comments and assessment materials

Comments

Signed in with GitHub; saved in repository Discussions

Mapping spec

Generated from the benchmark YAML; do not edit by hand

Dataset NameVariable NameVariable LabelTypeCodelist/Controlled TermsCoreComputational MethodOrigin
ADTTESTUDYIDStudy IdentifiercharacterCopy value from ADSL.STUDYID.Predecessor
ADTTEUSUBJIDUnique Subject IdentifiercharacterCopy value from ADSL.USUBJID.Predecessor
ADTTEPARAMCDParameter CodecharacterSet constant value "OS".Assigned
ADTTEPARAMParametercharacterSet constant value "Overall Survival".Assigned
ADTTESTARTDTTime-to-Event Origin DatenumericISO 8601Copy value from ADSL.RANDDT.Predecessor
ADTTEADTAnalysis DatenumericISO 8601If DEATHDT >= STARTDT then "DEATHDT"; If DEATHDT IS NOT NULL then "STARTDT"; If CENSORDT1 > STARTDT then "CENSORDT1"; otherwise "STARTDT".Derived
ADTTEAVALAnalysis ValuenumericWhole days from STARTDT to ADT, counting both endpoints.Derived
ADTTECNSRCensornumericIf DEATHDT IS NOT NULL then "0"; otherwise "1".Derived
ADTTEEVNTDESCEvent or Censoring DescriptioncharacterIf DEATHDT IS NOT NULL then "Death"; If CENSORDT1 > STARTDT then "Alive"; otherwise "Randomization".Derived
ADTTECNSDTDSCCensoring Date DescriptioncharacterIf DEATHDT IS NULL AND CENSORDT1 > STARTDT then "Alive During Study"; If DEATHDT IS NULL then "Randomization"; otherwise blank.Derived
ADTTESRCDOMSource DatacharacterIf DEATHDT IS NOT NULL then "ADRS"; otherwise "ADSL".Derived
ADTTESRCVARSource VariablecharacterIf DEATHDT IS NOT NULL then "ADT"; If CENSORDT1 > STARTDT then "LSTALVDT"; otherwise "RANDDT".Derived
ADTTESRCSEQSource Sequence NumbernumericIf DEATHDT IS NOT NULL then "DEATH_REC.ASEQ"; otherwise blank.Derived
VersionDateAuthorDescriptionReviewerSign-off
1.32026-09-29yamaa docs buildShow study codelist names, external dictionaries, and ISO 8601 formats in variable rows; list the document's codelist values and metadata
1.22026-09-29yamaa docs buildClassify ADaM copies from a declared input as Predecessor and recodes as Derived, and SDTM recodes of locally derived columns as Derived
1.12026-09-29yamaa docs buildResolve lookups into the donor record they select, and give every derivation shape a plain-language rule instead of its raw YAML
1.0yamaa docs buildInitial generation from spec.yaml

Agent prompt

The request an agent receives with the input files; each tier states no more than the one above

prompts/adam-adtte-os/full.md

Edit
34 lines

Following CDISC ADaM standards, use the provided ADSL and ADRS datasets to create an ADTTE dataset for overall survival, with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, CNSDTDSC, SRCDOM, SRCVAR, SRCSEQ

Use PARAMCD "OS" and PARAM "Overall Survival". STARTDT is the randomization date, the origin from which survival time is counted.

A qualifying death is a dated response record with code DEATH, result Y, and analysis flag Y; the earliest such date counts, with the lower sequence number breaking a tie on the same date.

ADT is the death date when it falls on or after STARTDT, and STARTDT itself when the death falls before it. With no death, ADT is the last-alive date when that date falls after randomization, else the randomization date. A death dated before randomization still counts as an event.

AVAL is the number of days from STARTDT to ADT, counting both days. CNSR is 0 for a death and 1 otherwise. EVNTDESC is Death for a death, Alive when censoring at the last-alive date, and Randomization when censoring at randomization. CNSDTDSC has no value for a death, and holds Alive During Study or Randomization for a censored record.

Trace ADT with SRCDOM ADRS, SRCVAR ADT, and the death record sequence for a death; with SRCDOM ADSL, SRCVAR LSTALVDT, and no sequence when censoring at the last-alive date; and with SRCDOM ADSL and SRCVAR RANDDT when censoring at randomization.

Read the source datasets from /app/input and save the completed dataset as /app/output/adtte.csv.

prompts/adam-adtte-os/conventions.md

Edit
20 lines

Following CDISC ADaM standards, use the provided ADSL and ADRS datasets to create an ADTTE dataset for overall survival, with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, CNSDTDSC, SRCDOM, SRCVAR, SRCSEQ

Use PARAMCD "OS" and PARAM "Overall Survival".

AVAL counts both STARTDT and ADT. CNSR is 0 for a death and 1 otherwise. EVNTDESC is Death, Alive, or Randomization. CNSDTDSC holds Alive During Study or Randomization, or has no value.

Trace ADT with SRCDOM, SRCVAR, and SRCSEQ, as one of: ADRS, ADT, and the death record sequence; ADSL, LSTALVDT, and no sequence; or ADSL and RANDDT.

Read the source datasets from /app/input and save the completed dataset as /app/output/adtte.csv.

prompts/adam-adtte-os/brief.md

Edit
10 lines

Following CDISC ADaM standards, use the provided ADSL and ADRS datasets to create an ADTTE dataset for overall survival, with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, CNSDTDSC, SRCDOM, SRCVAR, SRCSEQ

Read the source datasets from /app/input and save the completed dataset as /app/output/adtte.csv.

yamaa code

Runs the yamaa specification; its result must match the expected output

run.R

Edit
4 lines
library(yamaa)adtte <- yamaa_domain("spec.yaml")$outputadtte

run.py

Edit
4 lines
import yamaaadtte = yamaa.yamaa_domain("spec.yaml").outputadtte

Solution without yamaa

Written from the full prompt and the inputs alone; CI checks it reproduces the expected output

solutions/adam-adtte-os/result.R

Edit
70 lines
# Reference solution for the yamaa benchmark adam-adtte-os (R track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1.library(dplyr, warn.conflicts = FALSE)library(readr)adsl <- read_csv(  "/app/input/adsl.csv",  col_types = cols(RANDDT = col_date(), LSTALVDT = col_date(), .default = col_character()))adrs <- read_csv(  "/app/input/adrs.csv",  col_types = cols(ASEQ = col_integer(), ADT = col_date(), .default = col_character()))# The earliest dated qualifying death: code DEATH, result Y, flag Y.# On a tied date the lower sequence wins.death <- adrs |>  filter(PARAMCD == "DEATH", AVALC == "Y", ANL01FL == "Y", !is.na(ADT)) |>  arrange(STUDYID, USUBJID, ADT, ASEQ) |>  distinct(STUDYID, USUBJID, .keep_all = TRUE) |>  select(STUDYID, USUBJID, DEATHDT = ADT, DEATHSEQ = ASEQ)adtte <- adsl |>  left_join(death, by = c("STUDYID", "USUBJID")) |>  mutate(    STARTDT = RANDDT,    CENSORDT1 = LSTALVDT,    # A death before randomization still counts, moved up to it; with    # no death, the last-alive date when after randomization, else    # randomization itself.    ADT = case_when(      !is.na(DEATHDT) & !is.na(STARTDT) & DEATHDT >= STARTDT ~ DEATHDT,      !is.na(DEATHDT) ~ STARTDT,      !is.na(CENSORDT1) & !is.na(STARTDT) & CENSORDT1 > STARTDT ~ CENSORDT1,      .default = STARTDT    ),    AVAL = as.integer(ADT - STARTDT) + 1L,    CNSR = if_else(!is.na(DEATHDT), 0L, 1L),    EVNTDESC = case_when(      !is.na(DEATHDT) ~ "Death",      !is.na(CENSORDT1) & !is.na(STARTDT) & CENSORDT1 > STARTDT ~ "Alive",      .default = "Randomization"    ),    CNSDTDSC = case_when(      !is.na(DEATHDT) ~ NA_character_,      !is.na(CENSORDT1) & !is.na(STARTDT) & CENSORDT1 > STARTDT ~ "Alive During Study",      .default = "Randomization"    ),    SRCDOM = if_else(!is.na(DEATHDT), "ADRS", "ADSL"),    SRCVAR = case_when(      !is.na(DEATHDT) ~ "ADT",      !is.na(CENSORDT1) & !is.na(STARTDT) & CENSORDT1 > STARTDT ~ "LSTALVDT",      .default = "RANDDT"    ),    SRCSEQ = if_else(!is.na(DEATHDT), DEATHSEQ, NA_integer_),    PARAMCD = "OS",    PARAM = "Overall Survival"  ) |>  select(    STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR,    EVNTDESC, CNSDTDSC, SRCDOM, SRCVAR, SRCSEQ  ) |>  arrange(USUBJID)dir.create("/app/output", showWarnings = FALSE)write_csv(adtte, "/app/output/adtte.csv", na = "")

solutions/adam-adtte-os/result.py

Edit
98 lines
# Reference solution for the yamaa benchmark adam-adtte-os (Python track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1.from pathlib import Pathimport polars as pladsl = pl.read_csv("/app/input/adsl.csv", infer_schema=False, null_values="").with_columns(    pl.col("RANDDT").str.to_date(strict=False),    pl.col("LSTALVDT").str.to_date(strict=False),)adrs = pl.read_csv("/app/input/adrs.csv", infer_schema=False, null_values="").with_columns(    pl.col("ASEQ").cast(pl.Int64, strict=False),    pl.col("ADT").str.to_date(strict=False),)# The earliest dated qualifying death: code DEATH, result Y, flag Y.# On a tied date the lower sequence wins.death = (    adrs.filter(        (pl.col("PARAMCD") == "DEATH")        & (pl.col("AVALC") == "Y")        & (pl.col("ANL01FL") == "Y")        & pl.col("ADT").is_not_null()    )    .sort(["STUDYID", "USUBJID", "ADT", "ASEQ"])    .unique(["STUDYID", "USUBJID"], keep="first", maintain_order=True)    .select("STUDYID", "USUBJID", DEATHDT="ADT", DEATHSEQ="ASEQ"))adtte = (    adsl.join(death, on=["STUDYID", "USUBJID"], how="left", maintain_order="left")    .with_columns(        STARTDT=pl.col("RANDDT"),        CENSORDT1=pl.col("LSTALVDT"),    )    .with_columns(        # A death before randomization still counts, moved up to it; with        # no death, the last-alive date when after randomization, else        # randomization itself.        ADT=pl.when(pl.col("DEATHDT") >= pl.col("STARTDT"))        .then(pl.col("DEATHDT"))        .when(pl.col("DEATHDT").is_not_null())        .then(pl.col("STARTDT"))        .when(pl.col("CENSORDT1") > pl.col("STARTDT"))        .then(pl.col("CENSORDT1"))        .otherwise(pl.col("STARTDT")),    )    .with_columns(        AVAL=(pl.col("ADT") - pl.col("STARTDT")).dt.total_days() + 1,        CNSR=pl.when(pl.col("DEATHDT").is_not_null()).then(0).otherwise(1),        EVNTDESC=pl.when(pl.col("DEATHDT").is_not_null())        .then(pl.lit("Death"))        .when(pl.col("CENSORDT1") > pl.col("STARTDT"))        .then(pl.lit("Alive"))        .otherwise(pl.lit("Randomization")),        CNSDTDSC=pl.when(pl.col("DEATHDT").is_not_null())        .then(None)        .when(pl.col("CENSORDT1") > pl.col("STARTDT"))        .then(pl.lit("Alive During Study"))        .otherwise(pl.lit("Randomization")),        SRCDOM=pl.when(pl.col("DEATHDT").is_not_null())        .then(pl.lit("ADRS"))        .otherwise(pl.lit("ADSL")),        SRCVAR=pl.when(pl.col("DEATHDT").is_not_null())        .then(pl.lit("ADT"))        .when(pl.col("CENSORDT1") > pl.col("STARTDT"))        .then(pl.lit("LSTALVDT"))        .otherwise(pl.lit("RANDDT")),        SRCSEQ=pl.when(pl.col("DEATHDT").is_not_null())        .then(pl.col("DEATHSEQ"))        .otherwise(None),        PARAMCD=pl.lit("OS"),        PARAM=pl.lit("Overall Survival"),    )    .select(        "STUDYID",        "USUBJID",        "PARAMCD",        "PARAM",        "STARTDT",        "ADT",        "AVAL",        "CNSR",        "EVNTDESC",        "CNSDTDSC",        "SRCDOM",        "SRCVAR",        "SRCSEQ",    )    .sort("USUBJID"))Path("/app/output").mkdir(parents=True, exist_ok=True)adtte.write_csv("/app/output/adtte.csv", null_value="")

solutions/adam-adtte-os/result.sas

Edit
35 lines
/* opensas reference solution for the yamaa benchmark adam-adtte-os.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data adrs;  length STUDYID USUBJID PARAMCD AVALC ANL01FL ADT $1024;  infile "/app/input/adrs.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. PARAMCD : $char1024. AVALC : $char1024. ANL01FL : $char1024. ADT : $char1024. ASEQ;run;data adsl;  length STUDYID USUBJID RANDDT LSTALVDT $1024;  infile "/app/input/adsl.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. RANDDT : $char1024. LSTALVDT : $char1024.;run;proc sort data=adrs(where=(PARAMCD='DEATH' and AVALC='Y' and ANL01FL='Y' and ADT ne '')) out=events; by STUDYID USUBJID ADT ASEQ; run;data firstevent; set events; by STUDYID USUBJID; if first.USUBJID; rename ADT=DEATHDT ASEQ=DEATHSEQ; keep STUDYID USUBJID ADT ASEQ; run;proc sql; create table joined as select a.*,b.DEATHDT,b.DEATHSEQ from adsl as a left join firstevent as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID; quit;data result;  length PARAMCD $8 PARAM $40 STARTDT ADT $10 EVNTDESC CNSDTDSC $40 SRCDOM $4 SRCVAR $16;  set joined; PARAMCD='OS'; PARAM='Overall Survival'; STARTDT=RANDDT;  if not missing(DEATHDT) then do;    ADT=DEATHDT; if ADT<STARTDT then ADT=STARTDT;    CNSR=0; EVNTDESC='Death'; SRCDOM='ADRS'; SRCVAR='ADT'; SRCSEQ=DEATHSEQ;  end;  else if not missing(LSTALVDT) and LSTALVDT>RANDDT then do; ADT=LSTALVDT; CNSR=1; EVNTDESC='Alive'; CNSDTDSC='Alive During Study'; SRCDOM='ADSL'; SRCVAR='LSTALVDT'; end;  else do; ADT=RANDDT; CNSR=1; EVNTDESC='Randomization'; CNSDTDSC='Randomization'; SRCDOM='ADSL'; SRCVAR='RANDDT'; end;  if not missing(STARTDT) and not missing(ADT) then AVAL=input(ADT,yymmdd10.)-input(STARTDT,yymmdd10.)+1;run;proc sql;  create table final as select STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, CNSDTDSC, SRCDOM, SRCVAR, SRCSEQ from result;quit;proc export data=final outfile="/app/output/adtte.csv" dbms=csv replace; run;