obot.roadmap · assessment · 2026-07-15

nepExplorer → safety.viz: migration assessment

Can the SafetyGraphics kidney-function renderer become a Chart.js module in safety.viz? Short answer: yes, and it is a natural next renderer — its core chart is a direct kidney analog of the already-shipped hep-explorer. The work splits cleanly into a straightforward core scatter and a heavier patient-profile drill-down, and the main open question is data, not architecture.


Executive summary

What it is

A KDIGO-staged nephrotoxicity explorer: a per-participant scatter over creatinine change, with click-through patient-profile line charts. Clinically the kidney twin of eDISH.

Best source

The current Shiny v1.0.0 (R/ggplot2/plotly) is the behavioral source of truth. The deprecated JS v0.9.0 (D3 + RhoInc webcharts) is the legacy stack we are replacing — reference only.

Architecture fit

Canvas-first, not table-first. Reuses the proven hep-explorer / delta-delta module pattern — the KDIGO stage zones are a Chart.js background plugin.

2incarnations
~1.5kR lines (Shiny)
4measures missing
in demo data
2phases
GOrecommendation

Recommendation — proceed, phased, after a data decision

Migrate nepExplorer as a canvas-first module on the hep-explorer/delta-delta pattern, in two phases: (1) the KDIGO creatinine scatter (small–medium, demoable on today's pharmaverseadam data) and (2) the patient-profile drill-down (medium–large, blocked on kidney-specific measures the demo data lacks). Settle the demo-data question first (decision D1 below) — it gates phase 2's scope, not phase 1.


1 · Clinical purpose

What nepExplorer does: KDIGO nephrotoxicity screening

nepExplorer helps a safety reviewer spot acute declines in renal function in trial lab data. It is built around the KDIGO acute-kidney-injury (AKI) staging criteria, which classify severity from serum-creatinine change relative to baseline. The tool implements two of the KDIGO creatinine axes:

The main chart is a scatter, one point per participant, of each subject's maximum fold change (x) against maximum absolute change (y), laid over colored L-shaped KDIGO stage zones (white → yellow → orange → red for no-stage → 1 → 2 → 3). A companion summary table cross-tabulates the fold-change and absolute-change stage counts. It is the clinical sibling of hep-explorer's Hy's-Law eDISH quadrant plot — same "diagnostic scatter with clinical threshold zones + drill-down" shape, different organ.

Clicking a point opens a patient profile: a demographics panel plus small-multiple time-series line charts across five measure groups — serum creatinine & cystatin C, eGFR (creatinine- and cystatin-C–based), related electrolytes (BUN, sodium, potassium, bicarbonate, chloride, phosphate, calcium), blood pressure, and the urine albumin/creatinine ratio — with KDIGO reference lines, a measure picker, and a time-animation control on the main scatter.

Latent bug in the R source worth catching, not copying

The absolute-change staging in creatinine_data_fcn.R uses a case_when ordered >0.3 → >1.5 → >2.5. Because case_when returns the first match, every value above 0.3 is labeled "Stage 1" and the Stage 2/3 branches are unreachable. Migration should re-derive the staging from KDIGO intent (descending cut-points), not port the bug. Flag for the clinical owners.

2 · Two incarnations

Which source to migrate from

The repo has lived twice. Jeremy's recollection is correct: it began as a JavaScript renderer and was rewritten as a Shiny app in 2025 (NEWS: "Migrated from Javascript to R"). Both are on GitHub.

SOURCE OF TRUTH master · v1.0.0 · 2025

Shiny app (R)

ggplot2 + plotly scatter, gt summary table, plotly patient-profile line charts; a safetyGraphics module with a settings/mapping object. ~1,500 lines across ~13 R files. Actively the current product; carries the authoritative KDIGO logic, unit-conversion notes, and the richest feature set.

Use for: clinical logic, thresholds, feature scope, patient-profile design.

REFERENCE ONLY deprecated-js · v0.9.0

Legacy JS renderer

D3 + RhoInc webcharts, ~2,700 lines / 92 files, structured as kdigoScatterPlot + timeSeries with a settings-schema.json. This is exactly the legacy stack safety.viz exists to replace, and it predates the v1.0 feature set.

Use for: interaction/UX cues only. Do not port webcharts idioms.

Migrate from the Shiny v1.0.0 as the behavioral spec, exactly as hep-explorer reimplemented RhoInc's safety-eDISH behavior in Chart.js rather than lifting its webcharts code. The deprecated JS is a UX reference, not a code source. Recommendation · source selection

3 · Architecture fit

Canvas-first — it maps onto proven modules

safety.viz modules share a lifecycle (init / setData / setSettings / render / resize / destroy) and internal flow (checkInputs → configure → structureData → getScales/getPlugins → new Chart) over a shared control shell. nepExplorer is squarely canvas-first (a diagnostic scatter is the primary object), so it slots into the same pattern as the two closest precedents:

nepExplorer elementsafety.viz precedentMapping
KDIGO scatterhep-explorer eDISH / delta-deltaPer-participant scatter of two derived measures; direct reuse of scale + point-selection machinery.
KDIGO stage zoneshep-explorer quadrantPluginColored background regions + labels drawn by a Chart.js canvas plugin. Stage rectangles instead of Hy's-Law quadrant cut-lines — same technique.
value ÷ baseline, value − baselinehep-explorer ×ULN/×Baseline; delta-delta change-from-baselinestructureData reduces to one point per participant. Simpler here (single measure, baseline only).
measure_values matchinghep-explorer ALT/AST/TB/ALPMatch CREAT (and profile measures) from the measure column via a measure_values mapping.
Summary stage tabledelta-delta linked measure tableListing area in the shell; a stage cross-tab instead of a per-measure table.
Patient-profile line chartsresults-over-time / outlier-explorerTime-series line rendering exists; a multi-panel per-participant profile is net-new wiring on top of it.

The core scatter is well-trodden ground. The genuinely new surface is the patient-profile panel — five stacked small-multiple charts, a demographics table, a measure picker, and the scatter time-animation — which is richer than hep-explorer's participant-detail popover and has no one-to-one precedent yet.

4 · Data requirements

The data gap is the real constraint

nepExplorer needs a tall labs domain (id, value, measure, study-day, visit, visit-number, unit, baseline flag) plus vitals (blood pressure) and demographics. The clinical logic depends on a set of measure values. Here is how those line up against safety.viz's current demo data — pharmaverseadam (CDISC Pilot 01), shipped in v1.1.0 as site/data/adbds.csv:

Measure / featureUsed byIn pharmaverseadam?
Serum creatinineMain KDIGO scatter, profilePRESENT — core chart works today
Blood pressure (sys/dia)BP profile sub-chartPRESENT
BUN, Na, K, Cl, Ca, PhosphateRelated-electrolytes sub-chartPRESENT (6 of 7)
BicarbonateRelated-electrolytes sub-chartABSENT
eGFR (creatinine-based)eGFR profile sub-chartDERIVABLE — CKD-EPI from creatinine + age/sex
Cystatin CCreatinine sub-chart alt, eGFRcysABSENT — not derivable
eGFRcys (cystatin-C-based)eGFR profile sub-chartABSENT — needs cystatin C
Urine albumin/creatinine ratioUrine ACR sub-chartABSENT — distinct urinalysis measure

The takeaway: the core KDIGO scatter is fully demoable on today's data (creatinine is present), and two of the five profile sub-charts (BP, related electrolytes) largely work. But the kidney-specialized measures — cystatin C, eGFRcys, and the urine ACR — are genuinely absent from pharmaverseadam and cannot be derived. The RhoInc "renderer-specific" dataset that nepExplorer itself ships (and that was safety.viz's own demo data before the v1.1.0 pharmaverse migration) does carry all of them.

Unit dependence — the KDIGO absolute thresholds assume mg/dL

The fold-change axis (a ratio) is unit-independent and safe. But the absolute-change axis and the "≥ 4 mg/dL" rule are mg/dL-specific, and the demo creatinine is in µmol/L (≈88.4× larger). nepExplorer handles this with an explicit conversion step and warns that factors vary by sponsor. The Chart.js module must be unit-aware (convert to mg/dL for the absolute axis, or make thresholds unit-parameterized) — not hard, but a correctness trap if missed.

5 · Effort & phasing

Two phases, gated by the data decision

Phase 1 — KDIGO creatinine scatter

S–M
  • Canvas module on the hep-explorer/delta-delta pattern: per-participant scatter of fold change vs absolute change.
  • KDIGO stage-zone background plugin (adapt quadrantPlugin) + stage summary table in the shell.
  • Unit-aware standardization; point selection; tooltips; the coverage/evidence/API done-gate.
  • Demoable on current pharmaverseadam data. Comparable in size to delta-delta / the hep-explorer core.

Phase 2 — patient-profile drill-down

M–L
  • Multi-panel small-multiple time series (creatinine/eGFR/electrolytes/BP/urine ACR) reusing results-over-time / outlier-explorer line rendering.
  • Demographics panel, measure picker, KDIGO reference lines, optional scatter time-animation.
  • Blocked on the demo-data decision (D1): cystatin C, eGFRcys, and urine ACR sub-charts need data augmentation or a kidney-specific demo set.

The done-gate applies per phase: a module is not finished until the Pages site carries a gallery demo, an evidence page, and an API reference — factor that in beyond the code itself, as with every prior renderer.

6 · Risks

What could bite

RiskSevMitigation
Demo data lacks kidney-specialized measures (cystatin C, eGFRcys, urine ACR)highDecide D1: derive eGFR (CKD-EPI); retain RhoInc renderer-specific data as a nep-specific demo; or scope phase 2 to available measures.
Unit dependence of absolute KDIGO thresholdsmedBuild the module unit-aware; convert to mg/dL for the absolute axis; parameterize thresholds.
Clinical-logic fidelity (incl. the DELTA_STAGE bug)medRe-derive KDIGO staging from intent; validate against clinical owners; don't blind-port R.
Patient-profile scope — richest surface, no exact precedentmedPhase it; lean on results-over-time/outlier-explorer; treat time-animation as optional.
Scope creep — two features in one rendererlowShip phase 1 as a standalone renderer with its own done-gate before starting phase 2.

7 · Recommendation & decisions

Proceed — and one decision unblocks it

nepExplorer is a strong fit and a natural addition to the renderer set: it extends the eDISH-style diagnostic-scatter family from liver to kidney, reuses machinery that is already proven twice, and rounds out the organ-system coverage of the safety graphics suite. Filed as a standalone renderer requirement — hub #35 — sibling to hep-explorer (#30), beyond the v1.3 core set (#29), with features to mine from the upstream tracker under #33; migrating from the Shiny v1.0.0 as the behavioral spec, in the two phases above (framed as ~1–2 ultracode sessions).

D1

Demo data for the patient profile

Phase 2 needs cystatin C, eGFRcys, and urine ACR, which pharmaverseadam lacks. Options: (a) derive eGFR + retain the RhoInc renderer-specific dataset as a nep-specific demo; (b) augment pharmaverseadam with synthetic kidney measures; (c) scope phase 2 to available measures only.

Leaning: (a) — the RhoInc set was purpose-built for hep/nep and was safety.viz's own demo data pre-v1.1.0, so it is a known-good, self-consistent kidney demo without inventing data.

D2

Phase 1 alone, or bundle both?

Phase 1 (KDIGO scatter) is demoable today and independently valuable. Phase 2 (profile) is the larger, data-gated lift.

Leaning: ship phase 1 as a standalone renderer with its own done-gate; start phase 2 once D1 is settled.

D3

Time-animation of the scatter

nepExplorer animates the scatter across visits (plotly frames). Chart.js can do this, but it is custom and non-core.

Leaning: defer to a follow-up; not required for a v1 of the renderer.

This is an initial assessment for direction-setting, not a committed plan. On a GO, the next step is a hub requirement (Business Requirement → Data Requirement → Design) that resolves D1–D3 and decomposes phase 1 into safety.viz sub-issues.

Sources

What this is based on

upstream · master
SafetyGraphics/nepExplorer — Shiny v1.0.0
R/ modules, inst/config/nepExplorer.yaml, data-raw/ (meta + adbds), outline.md, NEWS.md
upstream · deprecated-js
SafetyGraphics/nepExplorer — legacy JS v0.9.0
src/kdigoScatterPlot, src/timeSeries, settings-schema.json (D3 + RhoInc webcharts)
target
jwildfire/safety.viz — Chart.js library
hep-explorer branch (eDISH precedent), delta-delta & results-over-time modules, src/data/schema/*, site/data/adbds.csv (pharmaverseadam demo)