direct: {op: direct, src: file.COLUMN}
constant: {op: constant, value: AE}
concat: {op: concat, src: [STUDYID, SITE, SUBJ], sep: "-"}   # null if any part is null
ct_map: {op: ct_map, src: SEV, codelist: C66769, values: {Mild: MILD}, ci: true, passthrough: false}
iso8601: {op: iso8601, src: [DATE_COL, TIME_COL], formats: ["%d-%b-%Y", "%Y-%m-%d"]}  # partial kept partial
study_day: {op: study_day, dtc: AESTDTC}      # needs DM.RFSTDTC, no day 0
seq: {op: seq, by: [USUBJID], sort: [AESTDTC, AETERM]}
epoch: {op: epoch, dtc: AESTDTC}              # epochs from study.yaml
lookup: {op: lookup, from: file.COLUMN, on: {LEFT: RIGHT}, agg: min|max|first|last, formats: [..], ci: true}
unit_convert: {op: unit_convert, test: LBTESTCD, src: RESULT, unit: LBORRESU, output: value|unit}
baseline_flag: {op: baseline_flag, dtc: VSDTC, result: VSORRES, by: [VSTESTCD]}
expr: {op: expr, value: "floor(years_between(BRTHDTC, RFICDTC))"}   # functions: floor ceil round abs min max upper lower trim len substr concat coalesce isnull num str fmt years_between days_between
supp (domain list): {op: supp, qnam: AEOTHSP, qlabel: Other Specify, src: OTHER_SPEC}
domain keys: source, joins [{file, on}], transpose {id, columns: {COL: {CONSTCOL: value}}}, row_filter (expr), distinct [cols], omit {VAR: reason}
reference rules: file.COL = raw column; @NAME or v_NAME (in expr) = derived variable; plain src names prefer raw columns; dtc/by/sort/test/unit/result prefer derived variables
helper variables start with an underscore and are not written to the dataset
