QT Safety Explorer: test evidence Experimental

Requirement-traced qualification evidence for the safety.viz qt-explorer module.

Scope
27 coverage rows55 distinct requirement IDs
Tests executed
860 automated checks118 browser ยท 742 unit
Result
pass all passing8 evidence screenshots
Generated
2026-10-04 12:03 UTC
Environment
linux 6.17.0-1022-azure ยท node v22.23.3 ยท playwright 1.61.1 ยท chromium 149.0.7827.55

Scope & approach

Traceability for the qt-explorer module (a Chart.js reimplementation of the SafetyGraphics/qtexplorer QT Safety Explorer, Phase 1, under #68; parent requirement obot.roadmap#36), per the convention in CONTRIBUTING.md. Requirement IDs use the module's condensed **QT-*** scheme cited in the source and test names โ€” QT-CT-* (central tendency: per-arm change over time, ฮ” / ฮ”ฮ”, CI band, reference line, peak-effect visit, ICH-E14 metric), QT-OUT-* (outlier scatter: absolute-QTc diagonals, change lines, arm marks, tooltip), QT-CAT-* (categorical by-arm exceedance), QT-CTRL-* (view / correction / statistic / display / timepoint / filter controls), and QT-DATA-* (data contract, cleaning, baseline/change derivation). The Phase-1 scope is QTcF/QTcB corrections plus heart rate; QTcI, PR/QRS/JT, per-subject drill-down, QT-RR hysteresis, the guided ICH-E14 workflow, and a moxifloxacin positive-control arm are deferred to Phase 2 (the CDISC Pilot ADEG carries none of them). A reviewed source requirement matrix (qt-explorer.md in the requirements repo) is not yet published for the Phase-1 rows, so their source-matrix column is pending; each row lists the module IDs its test covers.

Two later additions (#136) close legacy review asks against the original renderer: QT-CT-008 prints the central-tendency values and CI bounds the band already draws, and QT-CAUTION-* mounts the standing not-for-clinical-use caution and the conditional unblinding warning once into the module shell, where no view can blank them.

Each table row traces one requirement to the automated test(s) that evidence it: the Requirement column shows the reviewed requirement text and its ID, the source-matrix rows link back to the specification, the issue column links the implementing work, and the result column shows the recorded outcome of every matching test from the committed evidence.json with its captured screenshots. Browser evidence is captured at fixed conditions (1280ร—800, device scale 1) on the canonical Linux CI environment.

Browser evidence (Playwright โ€” tests/e2e/qt-explorer.spec.js)

13 requirement rows ยท 13 tests

RequirementSource matrix rowsIssueTests & evidence
QT-CTRL-001, QT-CTRL-002, QT-CTRL-003

QT-CTRL-001 A View control switches between the three views โ€” central tendency, outlier scatter, and categorical exceedance โ€” rendering one styled, clickable row per view with the active view marked.

QT-CTRL-002 A Correction control offers the configured parameter/correction options (default QTcF, QTcB, Heart Rate) and selects start_measure (QTcF) on first render, falling back to the first available measure when start_measure is unlisted.

QT-CTRL-003 Statistic (mean / median), display (ฮ” / ฮ”ฮ”), timepoint, and categorical filter controls narrow the view; applyFilters constrains rows on the active selections only, so an empty selection constrains nothing.

โ€”#68
  • pass QT-CTRL-001/QT-CTRL-002/QT-CTRL-003: renders the view, correction, statistic, display, and filter controls (#68)
Evidence screenshot QT-CTRL-001-central-tendency-delta.png
QT-CT-002, QT-CT-003, QT-CT-006

QT-CT-002 In mean mode the ฮ” series carries each arm's mean change with a two-sided 90% CI at every visit; in median mode the series carries no CI (lo/hi NaN). The chart draws per-arm lines with the CI band. Replicate readings (triplicate ECGs, for example) are averaged to one value per participant and visit before any mean, median or CI is computed, so each point's n is the number of participants.

QT-CT-003 A mode-labelled horizontal reference line marks the ICH-E14 threshold of regulatory concern on the central-tendency chart.

QT-CT-006 Each arm's peak-effect visit โ€” the visit of maximum effect โ€” is marked on the central-tendency chart.

โ€”#68
  • pass QT-CT-002/QT-CT-003/QT-CT-006: central tendency draws per-arm lines, a CI band, the reference line and the peak marker (#68)
QT-CT-004, QT-CT-005

QT-CT-004 The placebo-corrected ฮ”ฮ” mode drops the placebo arm and subtracts the placebo mean change at each visit; the placebo arm is auto-detected (matching /placebo/i) and ordered first.

QT-CT-005 The ICH-E14 metric is the largest upper bound of the two-sided 90% CI for the placebo-corrected mean difference (ฮ”ฮ”) across visits, flagged against the ~10 ms reference; the metric is reported only in mean + ฮ”ฮ” mode.

โ€”#68
  • pass QT-CT-004/QT-CT-005: ฮ”ฮ” drops placebo and reports the ICH-E14 metric above the reference (#68)
Evidence screenshot QT-CT-005-delta-delta-ich-metric.png
QT-CT-008

QT-CT-008 Beneath the central-tendency chart the view prints the plotted values as a table โ€” one row per visit and arm carrying the number of participants (replicate readings are averaged to one value per participant and visit first, exactly as for the chart), the plotted ฮ” / ฮ”ฮ” mean or median change, and the lower and upper bounds of the two-sided 90% CI the band draws โ€” built from the same series the chart consumes, so it honours the active correction, statistic, display mode, and filters; in median mode the CI columns read NA.

QT-CT-008#136
  • pass QT-CT-008: central tendency prints the plotted values and CI bounds beneath the chart (#136)
Evidence screenshot QT-CT-008-central-tendency-table.png
QT-OUT-002, QT-OUT-003, QT-OUT-004

QT-OUT-002 With a specific timepoint selected the scatter picks that visit's row per subject (matching a numeric-typed visit against a string timepoint); otherwise the maximum post-baseline value is used.

QT-OUT-003 The outlier scatter draws the 450/480/500 ms absolute-QTc diagonals, and in per-visit mode adds the 30/60 ms change-from-baseline horizontal lines; the absolute cut-lines apply only to QTc corrections.

QT-OUT-004 Each subject point is coloured and shaped by treatment arm, with a distinct cycling point style per arm.

โ€”#68
  • pass QT-OUT-002/QT-OUT-003/QT-OUT-004: the outlier scatter draws absolute diagonals, the zero line, and per-arm marks (#68)
Evidence screenshot QT-OUT-003-outlier-scatter-max.png
QT-OUT-003

QT-OUT-003 The outlier scatter draws the 450/480/500 ms absolute-QTc diagonals, and in per-visit mode adds the 30/60 ms change-from-baseline horizontal lines; the absolute cut-lines apply only to QTc corrections.

โ€”#68
  • pass QT-OUT-003: a specific visit adds the change-from-baseline lines (#68)
Evidence screenshot QT-OUT-003-outlier-scatter-max.png
QT-CAT-001, QT-CAT-002, QT-CAT-003

QT-CAT-001 The categorical view hides the chart and tabulates by-arm exceedance, with denominators of post-baseline subjects per arm, tracking each subject's maximum value and maximum change independently.

QT-CAT-002 Absolute-QTc thresholds (>450 / >480 / >500 ms) are counted per arm with a strict greater-than, so a value exactly at a threshold (e.g. 500) is excluded.

QT-CAT-003 Change-from-baseline thresholds (>30 / >60 ms) are counted per arm using each subject's maximum change, independently of the maximum value.

โ€”#68
  • pass QT-CAT-001/QT-CAT-002/QT-CAT-003: the categorical view hides the chart and tabulates by-arm exceedance (#68)
Evidence screenshot QT-CAT-002-categorical-table.png
QT-CT-007

QT-CT-007 Heart rate is offered as a central-tendency measure without the QTc ICH-E14 reference line (the ~10 ms threshold applies to QTc only).

โ€”#68
  • pass QT-CT-007: heart rate is offered in central tendency without the QTc reference line (#68)
QT-OUT-007

QT-OUT-007 Heart rate shows a QTc-only note in the outlier scatter view, since the absolute and change diagonals apply to QTc corrections only.

โ€”#68
  • pass QT-OUT-007: heart rate shows a QTc-only note in the outlier view (#68)
QT-CAUTION-001, QT-CAUTION-002

QT-CAUTION-001 The chart carries a standing caution โ€” exploratory tool, not validated for clinical use, confirm any signal with the validated ICH-E14 analyses โ€” rendered once into the module shell and shown in EVERY view, not written into the per-view footnote.

QT-CAUTION-002 When the bound data carries more than one treatment arm the chart carries a standing unblinding warning โ€” every view colours and labels participants by arm, so reading or sharing the results can unblind an ongoing study โ€” rendered once into the module shell beside the clinical caution; a single-arm or already-pooled extract raises no warning.

QT-CAUTION-001, QT-CAUTION-002#136
  • pass QT-CAUTION-001/QT-CAUTION-002: the standing caution and the unblinding warning ride every view (#136)
Evidence screenshot QT-CAUTION-001-standing-caution.png
QT-DATA-003

QT-DATA-003 cleanData drops rows with a missing or non-numeric value and reports the removed count; the on-page removed-count path leaves no page errors.

โ€”#68
  • pass QT-DATA-003: the participant note / removed-count path leaves no page errors (#68)
PPRF-QT-001, PPRF-QT-002
PPRF-QT-001, PPRF-QT-002 (participant-profile matrix)#99
  • pass PPRF-QT-001/PPRF-QT-002: clicking an outlier-scatter point opens the railed profile in observed ms with the 450 cut on QTcF (#99)
Evidence screenshot PPRF-QT-002-docked-profile-observed-ms.pngEvidence screenshot PPRF-QT-002-railed-profile-observed-ms.png
PPRF-QT-003, PPRF-QT-004
PPRF-QT-003, PPRF-QT-004 (participant-profile matrix)#99
  • pass PPRF-QT-003/PPRF-QT-004: empty clicks and view switches clear the rail, which idles on non-scatter views (#99)

Unit evidence (Vitest โ€” tests/unit/qt-explorer/)

14 requirement rows ยท 111 tests

RequirementSource matrix rowsIssueTests & evidence
QT-CFG-001..007 (settings normalization, z table, placebo resolution)

QT-CFG-001 syncSettings fills defaults and normalizes field-list settings (arrayifying single values).

QT-CFG-002 syncSettings sorts and numifies the threshold lists (absolute_thresholds, change_thresholds).

QT-CFG-003 syncSettings falls back start_measure to the first available measure when the configured start_measure is not in the measure list.

QT-CFG-004 syncSettings rejects an out-of-range ci_level, keeping the default (0.9).

QT-CFG-005 The VIEWS list and the TIMEPOINT_MAX ("maximum post-baseline") sentinel are stable constants the controls and timepoint selection read from.

QT-CFG-006 zForCi returns the two-sided z multiplier for a confidence level, interpolating between table points and clamping at the ends.

QT-CFG-007 resolvePlaceboArm prefers the explicit placebo setting when present and otherwise auto-detects the placebo arm by matching /placebo/i.

โ€”#68

configure.test.js

  • pass qt-explorer configure.resolvePlaceboArm QT-CFG-007: auto-detects /placebo/i otherwise
  • pass qt-explorer configure.resolvePlaceboArm QT-CFG-007: prefers the explicit setting when present
  • pass qt-explorer configure.syncSettings QT-CFG-001: fills defaults and normalizes field lists
  • pass qt-explorer configure.syncSettings QT-CFG-002: sorts and numifies threshold lists
  • pass qt-explorer configure.syncSettings QT-CFG-003: falls back start_measure to the first available measure
  • pass qt-explorer configure.syncSettings QT-CFG-004: rejects an out-of-range ci_level, keeping the default
  • pass qt-explorer configure.syncSettings QT-CFG-005: VIEWS and TIMEPOINT_MAX are stable
  • pass qt-explorer configure.zForCi QT-CFG-006: interpolates between table points and clamps the ends
  • pass qt-explorer configure.zForCi QT-CFG-006: returns the two-sided z for common levels
QT-DATA-005 (schema required columns)

QT-DATA-005 checkInputs validates the schema's required column settings (measure_col, value_col, arm_col, baseline_col) against the remapped names and throws naming every missing column; the baseline record is identified by the baseline flag (ABLFL=Y) and excluded from the post-baseline extremes, and when no baseline-flag column is present a change-of-0 row is treated as the baseline.

โ€”#68

checkInputs.test.js

  • pass markup safety: the missing-variable message QT-DATA-005: qt-explorer writes a missing column name as text, never as markup (#166)
  • pass qt-explorer checkInputs (QT-DATA-005) QT-DATA-005: an empty dataset reports all required columns missing
  • pass qt-explorer checkInputs (QT-DATA-005) QT-DATA-005: passes when every required column is present
  • pass qt-explorer checkInputs (QT-DATA-005) QT-DATA-005: throws naming each missing required column
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-005: flags the ABLFL=Y baseline record as not post-baseline
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-005: when the flag column is present, a post-baseline change-0 row with a missing flag stays post-baseline
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-005: with no baseline-flag column at all, a change-0 row is treated as the baseline
QT-DATA-003/004/006, QT-STAT-001/002 (cleaning, change derivation, stats)

QT-DATA-003 cleanData drops rows with a missing or non-numeric value and reports the removed count; the on-page removed-count path leaves no page errors.

QT-DATA-004 Change from baseline is the source change column when present, and is otherwise derived as value โˆ’ baseline when that column is blank.

QT-DATA-006 measuresPresent lists the distinct measures actually present in the data, so the correction control offers only measures the dataset carries.

QT-STAT-001 The unique helper keeps first-seen non-empty values, preserving order for the arm/visit/measure lists.

QT-STAT-002 The statistics helpers (mean, sd, quantile, median) match hand-computed values, backing the central-tendency means, the CI, and the categorical summaries.

โ€”#68

structureData.test.js

  • pass qt-explorer arms/visits/measures/filters QT-DATA-006: measuresPresent lists distinct measures
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-003: drops missing / non-numeric values with a count
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-004: derives change as value โˆ’ baseline when the change column is blank
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-004: keeps the source change when present
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-005: flags the ABLFL=Y baseline record as not post-baseline
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-005: when the flag column is present, a post-baseline change-0 row with a missing flag stays post-baseline
  • pass qt-explorer cleanData (QT-DATA-003/004) QT-DATA-005: with no baseline-flag column at all, a change-0 row is treated as the baseline
  • pass qt-explorer structureData statistics helpers QT-STAT-001: unique keeps first-seen non-empty values
  • pass qt-explorer structureData statistics helpers QT-STAT-002: mean/sd/quantile/median match hand values
QT-CT-001/002/004/005/006 (central-tendency series, ฮ”ฮ”, ICH-E14, peaks)

QT-CT-001 The central-tendency view plots per-arm change-from-baseline over ordered study visits (visits sorted by numeric visit), one series per treatment arm.

QT-CT-002 In mean mode the ฮ” series carries each arm's mean change with a two-sided 90% CI at every visit; in median mode the series carries no CI (lo/hi NaN). The chart draws per-arm lines with the CI band. Replicate readings (triplicate ECGs, for example) are averaged to one value per participant and visit before any mean, median or CI is computed, so each point's n is the number of participants.

QT-CT-004 The placebo-corrected ฮ”ฮ” mode drops the placebo arm and subtracts the placebo mean change at each visit; the placebo arm is auto-detected (matching /placebo/i) and ordered first.

QT-CT-005 The ICH-E14 metric is the largest upper bound of the two-sided 90% CI for the placebo-corrected mean difference (ฮ”ฮ”) across visits, flagged against the ~10 ms reference; the metric is reported only in mean + ฮ”ฮ” mode.

QT-CT-006 Each arm's peak-effect visit โ€” the visit of maximum effect โ€” is marked on the central-tendency chart.

โ€”#68

structureData.test.js

  • pass qt-explorer arms/visits/measures/filters QT-CT-001: orderVisits sorts by numeric visit
  • pass qt-explorer arms/visits/measures/filters QT-CT-004: armsPresent puts the placebo arm first
  • pass qt-explorer arms/visits/measures/filters QT-CT-004: placeboArmFor auto-detects /placebo/i
  • pass qt-explorer centralTendencySeries (QT-CT-001/004/005) QT-CT-002: median mode carries no CI (lo/hi NaN)
  • pass qt-explorer centralTendencySeries (QT-CT-001/004/005) QT-CT-002: replicate readings average to one value per participant and visit before the mean and CI, and n counts participants (#166)
  • pass qt-explorer centralTendencySeries (QT-CT-001/004/005) QT-CT-002: ฮ” mean series carries per-arm means and a 90% CI
  • pass qt-explorer centralTendencySeries (QT-CT-001/004/005) QT-CT-004: ฮ”ฮ” drops placebo and subtracts placebo mean change
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-001: correctionSuffix maps the QTc formulas
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-002: isQtcMeasure + measureUnit distinguish QTc from HR
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-003: central axis title carries mode prefix + correction suffix
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-004: scatter titles mirror the mockup
  • pass qt-explorer ICH-E14 metric + peak visit (QT-CT-005/006) QT-CT-005: ICH-E14 metric is the largest ฮ”ฮ” upper CI bound, flagged vs 10 ms
  • pass qt-explorer ICH-E14 metric + peak visit (QT-CT-005/006) QT-CT-005: metric is empty outside mean + ฮ”ฮ”
  • pass qt-explorer ICH-E14 metric + peak visit (QT-CT-005/006) QT-CT-006: peakVisits returns each arm peak
Evidence screenshot QT-CT-005-delta-delta-ich-metric.png
QT-CT-008 (printed central-tendency values table)

QT-CT-008 Beneath the central-tendency chart the view prints the plotted values as a table โ€” one row per visit and arm carrying the number of participants (replicate readings are averaged to one value per participant and visit first, exactly as for the chart), the plotted ฮ” / ฮ”ฮ” mean or median change, and the lower and upper bounds of the two-sided 90% CI the band draws โ€” built from the same series the chart consumes, so it honours the active correction, statistic, display mode, and filters; in median mode the CI columns read NA.

QT-CT-008#136

central-table.test.js

  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: median mode prints NA for the CI bounds (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: the categorical view still shows only the exceedance table (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: the central-tendency view prints one row per visit and arm beneath the visible chart (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: the header carries the configured confidence level (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: the printed values are the plotted mean and the CI bounds the band draws (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: the printed values honour the active correction (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: the printed values honour the active filters (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: with triplicate ECGs n is the number of participants and the printed values do not move (#166)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: ฮ”ฮ” mode prints the placebo-corrected difference and drops the placebo arm (#136)
  • pass qt-explorer printed central-tendency values (QT-CT-008) QT-CT-008: ฮ”ฮ” without a placebo arm renders no table (#136)
Evidence screenshot QT-CT-008-central-tendency-table.png
QT-OUT-001/002 (subject points, max post-baseline vs visit)

QT-OUT-001 The outlier scatter plots one point per subject at the subject's maximum post-baseline QTc value against their baseline QTc.

QT-OUT-002 With a specific timepoint selected the scatter picks that visit's row per subject (matching a numeric-typed visit against a string timepoint); otherwise the maximum post-baseline value is used.

โ€”#68

structureData.test.js

  • pass qt-explorer subjectPoints (QT-OUT-001/002) QT-OUT-002: a specific visit picks that visit row
  • pass qt-explorer subjectPoints (QT-OUT-001/002) QT-OUT-002: matches a numeric-typed visit against a string timepoint
  • pass qt-explorer subjectPoints (QT-OUT-001/002) QT-OUT-002: maximum post-baseline picks the max absolute value row
QT-CAT-001/002/003 (by-arm exceedance counts and percents)

QT-CAT-001 The categorical view hides the chart and tabulates by-arm exceedance, with denominators of post-baseline subjects per arm, tracking each subject's maximum value and maximum change independently.

QT-CAT-002 Absolute-QTc thresholds (>450 / >480 / >500 ms) are counted per arm with a strict greater-than, so a value exactly at a threshold (e.g. 500) is excluded.

QT-CAT-003 Change-from-baseline thresholds (>30 / >60 ms) are counted per arm using each subject's maximum change, independently of the maximum value.

โ€”#68

structureData.test.js

  • pass qt-explorer classifyThresholds (QT-CAT-002/003) QT-CAT-001: denominators are post-baseline subjects per arm
  • pass qt-explorer classifyThresholds (QT-CAT-002/003) QT-CAT-002: absolute >450 counts D1(470) and D2(500) in Drug
  • pass qt-explorer classifyThresholds (QT-CAT-002/003) QT-CAT-002: absolute >500 excludes the boundary value 500 (strict >)
  • pass qt-explorer classifyThresholds (QT-CAT-002/003) QT-CAT-003: change >60 counts only D2(70) using max change independently
  • pass qt-explorer subjectExtremes (QT-CAT-001) QT-CAT-001: tracks max value and max change independently
Evidence screenshot QT-CAT-002-categorical-table.png
QT-SCL-001..008 (correction suffix, axis titles, domains, arm marks)

QT-SCL-001 correctionSuffix maps the QTc formulas to their axis-title suffix (QTcF โ†’ Fridericia, QTcB โ†’ Bazett).

QT-SCL-002 isQtcMeasure and measureUnit distinguish a QTc correction (msec, cut-lines apply) from heart rate (bpm, no QTc cut-lines).

QT-SCL-003 The central-tendency axis title carries the mode prefix (ฮ” / ฮ”ฮ”) plus the correction suffix and units.

QT-SCL-004 The outlier-scatter axis titles mirror the design mockup (baseline QTc on x, change-from-baseline on y, with the correction suffix).

QT-SCL-005 formatNumber trims trailing zeros and guards non-finite values in tick and tooltip formatting.

QT-SCL-006 formatSigned signs the change value with a real minus glyph (โˆ’) for the tooltip and change labels.

QT-SCL-007 paddedDomain covers the data values plus any forced inclusions (the threshold cut-lines), and handles an empty or degenerate sample without collapsing.

QT-SCL-008 The scatter assigns a distinct cycling point style per arm so arms are distinguishable by shape as well as colour.

โ€”#68

getScales.test.js

  • pass qt-explorer getScales arm point styles (QT-OUT-004) QT-SCL-008: assigns a distinct cycling point style per arm
  • pass qt-explorer getScales formatting + domains QT-SCL-005: formatNumber trims and guards non-finite
  • pass qt-explorer getScales formatting + domains QT-SCL-006: formatSigned signs the change with a real minus glyph
  • pass qt-explorer getScales formatting + domains QT-SCL-007: paddedDomain covers values and forced inclusions
  • pass qt-explorer getScales formatting + domains QT-SCL-007: paddedDomain handles an empty / degenerate sample
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-001: correctionSuffix maps the QTc formulas
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-002: isQtcMeasure + measureUnit distinguish QTc from HR
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-003: central axis title carries mode prefix + correction suffix
  • pass qt-explorer getScales titles + units (QT-OUT-005, QT-CT-001) QT-SCL-004: scatter titles mirror the mockup
QT-PLG-001/002/003 (color scale, rgba, scatter tooltip)

QT-PLG-001 hexToRgba converts a hex colour to an rgba string at a given opacity, backing the CI band and point fills.

QT-PLG-002 armColorScale assigns stable, cycling colours per arm, consistent across the central-tendency, scatter, and categorical views.

QT-PLG-003 scatterTooltip renders the outlier-point tooltip lines โ€” id, arm, baseline, value, change, and visit.

โ€”#68

getPlugins.test.js

  • pass qt-explorer getPlugins helpers (QT-OUT-004/006) QT-PLG-001: hexToRgba converts to rgba at opacity
  • pass qt-explorer getPlugins helpers (QT-OUT-004/006) QT-PLG-002: armColorScale assigns stable cycling colors
  • pass qt-explorer getPlugins helpers (QT-OUT-004/006) QT-PLG-003: scatterTooltip lists id, arm, baseline, value, change, visit
QT-API-001 (module export)

QT-API-001 The public safety.viz collection exposes a qtExplorer factory function, identical to the named qtExplorer export.

โ€”#68

export.test.js

  • pass qt-explorer module export QT-API-001: the public collection exposes the qtExplorer factory
QT-CAUTION-001, QT-CAUTION-002 (standing caution, unblinding gate)

QT-CAUTION-001 The chart carries a standing caution โ€” exploratory tool, not validated for clinical use, confirm any signal with the validated ICH-E14 analyses โ€” rendered once into the module shell and shown in EVERY view, not written into the per-view footnote.

QT-CAUTION-002 When the bound data carries more than one treatment arm the chart carries a standing unblinding warning โ€” every view colours and labels participants by arm, so reading or sharing the results can unblind an ongoing study โ€” rendered once into the module shell beside the clinical caution; a single-arm or already-pooled extract raises no warning.

QT-CAUTION-001, QT-CAUTION-002#136

configure.test.js, cautions.test.js

  • pass qt-explorer standing caution (QT-CAUTION-001) QT-CAUTION-001: the caution is not duplicated in any view footnote (#136)
  • pass qt-explorer standing caution (QT-CAUTION-001) QT-CAUTION-001: the caution survives every view switch (#136)
  • pass qt-explorer standing caution (QT-CAUTION-001) QT-CAUTION-001: the caution survives the heart-rate paths that blank the footnote (#136)
  • pass qt-explorer standing caution (QT-CAUTION-001) QT-CAUTION-001: the caution survives the no-usable-rows render (#136)
  • pass qt-explorer standing caution (QT-CAUTION-001) QT-CAUTION-001: the not-for-clinical-use caution is mounted once into the module shell (#136)
  • pass qt-explorer standing cautions QT-CAUTION-001: the standing caution names the tool exploratory and not validated for clinical use (#136)
  • pass qt-explorer standing cautions QT-CAUTION-002: the unblinding warning applies only when more than one arm is present (#136)
  • pass qt-explorer standing cautions QT-CAUTION-002: the unblinding warning names treatment assignment and the blinding plan (#136)
  • pass qt-explorer unblinding warning (QT-CAUTION-002) QT-CAUTION-002: multi-arm data carries the unblinding warning on every view (#136)
  • pass qt-explorer unblinding warning (QT-CAUTION-002) QT-CAUTION-002: single-arm data raises no unblinding warning (#136)
  • pass qt-explorer unblinding warning (QT-CAUTION-002) QT-CAUTION-002: the warning follows the data when a second arm arrives (#136)
Evidence screenshot QT-CAUTION-001-standing-caution.png
PPRF-QT-001..004 (rail adoption, interval-measure mapping, clear paths)
PPRF-QT-001..004 (participant-profile matrix)#99

profile-adoption.test.js

  • pass qt-explorer interval-measure mapping (PPRF-QT-002) PPRF-QT-002: feed rows survive the unit-ULN synthesis โ€” observed ms on the ULN scale, nothing drops
  • pass qt-explorer interval-measure mapping (PPRF-QT-002) PPRF-QT-002: maps the profile settings for interval measures โ€” identity keys, per-QTc cuts, observed-ms labels
  • pass qt-explorer interval-measure mapping (PPRF-QT-002) PPRF-QT-002: the railed spaghetti carries the 450 ms cut on QTc series and no cut on Heart Rate
  • pass qt-explorer participant-profile adoption (PPRF-QT-001) PPRF-QT-001: clicking an outlier-scatter point feeds the rail the full profile via the shell root
  • pass qt-explorer participant-profile adoption (PPRF-QT-001) PPRF-QT-001: mounts the rail by default (config-on) into the shell profile slot
  • pass qt-explorer participant-profile adoption (PPRF-QT-001) PPRF-QT-001: profile: false leaves the slot empty and mounts no dock
  • pass qt-explorer participant-profile adoption (PPRF-QT-001) PPRF-QT-004: no listing is added โ€” the rail is the only drill-down surface
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-001: destroy tears the rail down with the instance
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-001: setSettings({profile: false}) unmounts a live dock; re-enabling remounts it
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-003: a view switch clears the selection and the rail (render preamble); non-scatter views idle
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-003: an empty-canvas click clears the selection and empties the rail
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-003: dock Clear empties an externally-fed cohort the host never selected (PPRF-11)
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-003: the rail Clear affordance routes through the host clear path
  • pass qt-explorer rail clear paths (PPRF-QT-003, PPRF-11) PPRF-QT-004: stepping an externally-fed cohort emphasizes the stepped point without re-dispatching
Evidence screenshot PPRF-QT-002-docked-profile-observed-ms.pngEvidence screenshot PPRF-QT-002-railed-profile-observed-ms.png
QT-FILT-001..004 (shared filter contract)

QT-FILT-001 A filter spec accepts start, all and multiple alongside value_col and label, and means the same thing here as in every other renderer. A spec that declares only value_col and label behaves exactly as it did before: a single-value dropdown whose first option is "All". A filter whose column the data lacks draws no control and places no restriction on the chart, whatever its start.

QT-FILT-002 start names the value the filter opens on and changes nothing else: the "All" option stays on offer unless the spec says all: false. A start of 0 or false is a real column value, not an absent start. A start value the data lacks is dropped with a console warning naming the filter and the value, and the filter opens on "All" โ€” or on its first value when "All" is not offered. Reset returns the filter to this opening selection.

QT-FILT-003 all: false removes the "All" option so a value is always selected: the first value, unless start names another, and the chart opens filtered to it. The key is read as written and is never truthiness-tested, because false is the value that does something.

QT-FILT-004 multiple: true renders a checkbox multiselect instead of a dropdown, and the rows shown are those matching ANY selected value. Its state is null (everything selected, no restriction) or an array of values. Start values the data lacks are dropped, and when none remain the filter places no restriction; a list the reader empties by hand selects nothing.

QT-FILT-001, QT-FILT-002, QT-FILT-003, QT-FILT-004#166

../shared/filter-contract.test.js

  • pass 'qt-explorer': the shared filter contract QT-FILT-001: a filter whose column the data lacks draws no control and leaves no restriction behind (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-001: a plain filter offers All, opens unfiltered and draws every participant (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-001: choosing a value filters the chart, and choosing All brings everyone back (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: a start value opens the chart filtered, with All still on offer (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: a start value the data lacks warns by name and opens on All (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: after a reader's own choice, setSettings still leaves the control and the chart agreeing (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: Reset returns the filter to its opening selection (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: Reset squares a start value the data lacks with the data, exactly as load does (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: setSettings leaves the chart on its opening selection (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-002: when later data brings the filter's column, the filter opens on its start value (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-003: all:false offers no All, selects the first value and filters to it (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-003: all:false with a start the data lacks falls back to the first value (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-004: a multiple filter emptied by hand keeps meaning nothing is selected (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-004: multiple drops start values the data lacks, and keeps the rest (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-004: multiple opens on its start list and draws every checked value (#166)
  • pass 'qt-explorer': the shared filter contract QT-FILT-004: multiple with no start value left in the data places no restriction (#166)
  • pass schemas: a filter spec QT-FILT-001: the qt-explorer schema lists start, all and multiple on a filter spec (#166)
QT-CTRL-004 (whole-chart reset, back to the central view)

QT-CTRL-004 A reset control at the foot of the control panel restores every settings-derived control to its configured default, re-pins the correction to one the bound data carries, clears the scatter selection and the docked profile, and re-renders. Reset is a WHOLE-chart reset: it also returns the reader to the central-tendency view, rather than resetting the controls within whichever view is open.

QT-CTRL-004#136

reset.test.js

  • pass qt-explorer reset control (QT-CTRL-004) QT-CTRL-004: a full-width reset control sits at the foot of the control panel (#136)
  • pass qt-explorer reset control (QT-CTRL-004) QT-CTRL-004: reset clears the scatter selection, the docked profile, and the live charts (#136)
  • pass qt-explorer reset control (QT-CTRL-004) QT-CTRL-004: reset leaves the standing cautions and the central-tendency CI table intact (#136)
  • pass qt-explorer reset control (QT-CTRL-004) QT-CTRL-004: reset re-pins the correction to one the data carries, not the configured start (#136)
  • pass qt-explorer reset control (QT-CTRL-004) QT-CTRL-004: reset restores the correction, statistic, display type and filters (#136)
  • pass qt-explorer reset control (QT-CTRL-004) QT-CTRL-004: reset returns the reader to the central-tendency view and the default timepoint (#136)

Railed participant profile (#99, PPRF-QT)

0 requirement rows ยท 0 tests

RequirementSource matrix rowsIssueTests & evidence

Visual evidence

Every screenshot below is a committed baseline: the same PNG is the visual-regression baseline the browser suite asserts against and the evidence artifact shown here. Click any capture for the full-resolution image.

Source-matrix routing status

No reviewed source requirement matrix exists yet for this module (the config's qt-explorer.md matrix link resolves once it is published in the requirements repo), so routing is against the Phase-1 port spec's scope rather than matrix rows.

View-selector rollout (VIEW-2, #76)

The View selector (QT-CTRL-001) is now the shared shell builder renderViewSelector โ€” the module-scoped qt-view-* CSS + builder were removed with no visual or behavioral change (option class names moved to the neutral sv-view-* namespace). See view-selector-inventory.md.

Reproducing this report

The evidence set is regenerated from a full test run and committed with the code it qualifies; CI fails when they drift. To verify or rebuild it:

npm ci
npm run evidence:check   # compare a fresh run against the committed evidence
npm run evidence         # regenerate docs/evidence/qt-explorer/evidence.json

Screenshot baselines are canonical to the Linux CI runner; the repository's Update evidence baselines workflow is the authoritative way to refresh them. See CONTRIBUTING.md for the traceability convention.