safety.viz ↔ gsm.safety widget parity
2026-08-15 · derived from both repos' source, not estimated · supports hub requirements #164 and #165
13 renderers, 9 widgets. Four renderers have no R widget, and all nine existing widgets render through a vendored v1.4.0 bundle — two minor releases behind the current v1.6.0. The rule this report supports (@jwildfire, 2026-08-15): every renderer gets an R widget.
The stale-widget defect, named plainly
Widget_HepExplorer() has been rendering the eDISH composite view with a peak computation safety.viz corrected in v1.5.0. The v1.4.0 bundle excludes the baseline record from on-treatment peaks by study day; a participant with no day-0 record gets their own baseline counted as an on-treatment peak (safety.viz#91 fixed this to identity-based exclusion — 24 of 318 demo participants affected). Any R report built with the released gsm.safety v1.0.0 reproduces the pre-fix chart. A widget wrapping a stale renderer is worse than a missing one: it looks done and draws a wrong chart in a clinical display.
Matrix
| safety.viz renderer | Shipped in | gsm.safety widget | Status |
|---|---|---|---|
| histogram | ≤ v1.1 | Widget_Histogram | stale — v1.4.0 bundle; missing profile drill-down (v1.5.0) |
| shift-plot | ≤ v1.1 | Widget_ShiftPlot | stale — v1.4.0 bundle; missing profile drill-down |
| delta-delta | ≤ v1.1 | Widget_DeltaDelta | stale — v1.4.0 bundle; missing profile drill-down |
| results-over-time | ≤ v1.1 | Widget_ResultsOverTime | stale — v1.4.0 bundle |
| outlier-explorer | ≤ v1.1 | Widget_OutlierExplorer | stale — v1.4.0 bundle; missing profile drill-down |
| ae-timelines | ≤ v1.1 | Widget_AeTimelines | stale — v1.4.0 bundle |
| ae-explorer | v1.4.0 | Widget_AeExplorer | stale — v1.4.0 bundle |
| hep-explorer | v1.2.0 | Widget_HepExplorer | stale + defect — pre-fix peak computation; vendored schema also stale |
| qt-explorer | v1.4.0 | Widget_QtExplorer | stale — v1.4.0 bundle; missing view-selector (v1.4.1), profile drill-down (v1.5.0) |
| hep-waterfall | v1.5.0 | — | missing |
| participant-profile | v1.5.0 | — | missing — R surface needs design (multi-domain payload, event-driven mount) |
| nep-explorer | v1.6.0 | — | missing |
| time-to-event | unreleased (dev → v1.7.0) | — | missing — widget requirement filed ahead per the new rule |
How staleness was determined
- One vendored bundle serves every widget:
inst/htmlwidgets/lib/safety.viz-1.4.0/, declared in eachWidget_*.yaml. safety.viz released v1.4.1, v1.5.0, and v1.6.0 after it;git diff v1.4.0..devshows every one of the nine wrapped renderers changed. - Vendored data contracts (
inst/schema/*.json) hashed against safety.viz v1.4.0 and dev: eight of nine unchanged upstream;hep-explorer.jsonmatches v1.4.0 and is stale against dev. - Example data verified per file:
adegis current (re-vendored 2026-07-21 with the corrected QTc derivation; distributions match safety.viz exactly — no data defect).adbdspredates theAKI-*cohort (0 vs 368 rows) the nep-explorer demo depends on.
Why it drifted, and what now prevents it
- gsm.safety v1.0.0 was genuinely at full parity with safety.viz v1.4.0 on release day — its README said "one for every renderer the library ships", and it was true. Nothing forced the repos back together afterward: renderer requirements said "gsm.safety widget adoption follows in that package's own cadence" — an intention, never a filing.
- The rule: the renderer done-gate gains a fourth pillar — the gsm.safety widget delivered, or filed as a milestoned requirement, named in the renderer's requirement from the start (safety.viz
CONTRIBUTING.md, obot.agentAGENTS.md+ RC framework, hub requirement skill). - The check: gsm.safety CI (
safety-viz-parity) compares the latest safety.viz release against the package — bundle version current, every exported renderer wrapped. Blocking on PRs; deferrals live in a committed allowlist where every entry must cite a filed requirement URL. Weekly, drift auto-files an adoption issue in gsm.safety — no cross-repo token needed, because the dependent repo checks its own upstream and nags itself. safety.viz CI takes no reverse dependency, deliberately: a renderer PR cannot contain the R widget, and wedging the library on its wrapper would be backwards. - The declaration:
Config/safetyviz/versionin DESCRIPTION, kept honest by an offline test tying it to the vendored bundle and every widget binding.
Delivery
- Catch-up implementation (bundle → v1.6.0,
Widget_HepWaterfall(),Widget_NepExplorer(), parity guard): gsm.safety#51 — held unmerged while RC #52 (v1.1.0) is in review; ships as v1.2.0 under hub #164. - Deferred wraps (participant-profile design, time-to-event): hub #165 (gsm.safety v1.3.0).
- Done-gate rule edits: safety.viz#132, obot.agent#103.
Drafted by Claude Code using Fable 5 in an unattended session — not yet reviewed by @jwildfire.