Design document for Requirement #1 · obot.roadmap
STATUS: Signed off 2026-07-04 · drafted 2026-07-02, ported to obot.roadmap 2026-07-03, updated 2026-07-04 (qcthat alignment) · drafted by Claude Code using Fable 5, reviewed by @jwildfire#1 consolidates the
nine legacy safetyGraphics renderers into one modern Chart.js library at
jwildfire/safety.viz, consumed by
Widget_*.R htmlwidgets in gsm.safety — mirroring how
gsm.kri consumes
gsm.viz. This document settles the
repo layout, module contract, stack, traceability approach, and the distribution decision.
Non-goals: no shared-utility layer until the second renderer forces one; no npm publication in the first iteration; no visual-regression or accessibility hardening in the pilot (tracked as explicit follow-ups).
safety.agent matrices safety.viz consumers
(requirement IDs) ──keys──► src/<chart>/ modules ──esbuild──► dist/safety.viz-{version}/
Vitest (unit) ├─► gsm.safety inst/htmlwidgets/lib/ (vendored)
Playwright (browser evidence) └─► examples/ static demo pages
Mirrors gsm.viz's structure, with the test stack modernized:
safety.viz/ ├── src/ │ ├── main.js # public module collection; registers shared Chart.js plugins │ ├── histogram.js # public entrypoint for one chart (one pair per renderer) │ ├── histogram/ │ │ ├── checkInputs.js # validate data/config against JSON schemas │ │ ├── configure.js # defaults + metadata + user-config merge │ │ ├── structureData.js # input records → Chart.js datasets │ │ ├── getPlugins.js # tooltips, annotations, custom plugins │ │ ├── getScales.js # axes │ │ ├── updateData.js # update lifecycle │ │ └── updateConfig.js │ └── data/schema/ # JSON schemas — machine-readable data contracts ├── tests/ │ ├── unit/ # Vitest, requirement-ID-keyed describe/it names │ └── e2e/ # Playwright specs + fixtures + demo test pages ├── examples/ # static example pages (GitHub Pages later) ├── dist/ # committed esbuild bundles (see Distribution) ├── package.json · vitest.config.js · playwright.config.js └── .github/workflows/ci.yml
Every renderer module exposes the lifecycle API proven by the histogram pilot as its public surface — this is what widget bindings and test harnesses call:
| Method | Behavior |
|---|---|
init(el, data, settings) | Mount into a container element; validate inputs (checkInputs) and render. |
setData(data) / setSettings(settings) | Replace inputs; re-derive config and datasets. |
render() / resize() | Redraw; respond to container size changes. |
destroy() | Tear down the Chart.js instance and listeners; container returns to pre-init state. |
Internally each module follows the gsm.viz flow
(checkInputs → configure → structureData → getPlugins/getScales → new Chart(...)),
so the file layout stays recognizably parallel to the reference implementation even though the
public API is the pilot's lifecycle shape.
| Choice | What | Rationale / deltas from references |
|---|---|---|
| Charting | Chart.js v4 (+ plugins as needed) | Pilot already runs 4.4.x; gsm.viz is on v3 — we take the newer major from the start. |
| Bundler | esbuild | Same as gsm.viz. The pilot used rollup; esbuild replaces it at extraction time (single-tool, fast, ESM-first). |
| Unit tests | Vitest | gsm.viz uses Jest 29 + jsdom; Vitest is the ESM-native equivalent with the same assertion style. |
| Browser evidence | Playwright | Carries over the pilot's 10-spec harness pattern; produces the browser-verifiable evidence the migration workflow requires. |
| Formatting | Prettier | Same as gsm.viz and the pilot. |
(#N) style,
e.g. test('SH-CTRL-004: normal range checkbox displays overlay when available (#N)', …)
— where #N is the safety.viz implementation issue for the work (the
sub-issues filed via requirement-tasks).unit rows → Vitest;
browser rows → Playwright.docs/, the qcthat-style traceability artifact on the JS side.Recommendation: vendored, versioned dist/ directory; defer npm.
dist/safety.viz-{version}/safety.viz.js (IIFE/UMD for widgets)
and an ESM build; the versioned directory is committed to the repo.inst/htmlwidgets/lib/safety.viz-{version}/ —
the exact precedent set by gsm.kri, which ships
inst/htmlwidgets/lib/gsm.viz-2.3.0/.One GitHub Actions workflow on PRs and main: npm ci → build →
Vitest → Playwright (with browser install step). A drift check fails the build if
dist/ doesn't match a fresh build of src/.
dev
(merged PR #1) under Requirement #2
slice ② — see 2_design.html.jwildfire/safety.viz alongside the rest of the project
(the obot-claw org is archived); nothing in this design depends on the owner.