---
title: "Safety review checklist"
description: "A printable pre-ship checklist for any screen that displays, interprets or collects health information. Each item names the rule it enforces."
url: "https://opsinjs.pensievelabs.org/health/safety-review-checklist"
source: "https://opsinjs.pensievelabs.org/health/safety-review-checklist.md"
section: "Health"
kind: "health"
evidence: "opinion"
reviewed: "2026-09-20"
reviewer: "design"
aliases: ["pre-ship checklist", "safety review", "sign off", "clinical review", "launch checklist"]
implements: ["result-card", "range-bar", "status-pill", "alert-banner", "care-card", "value", "relative-time", "consent-sheet", "disclaimer-note"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="health" />

## What this means [#what-this-means]

This page is the operational form of the arguments made across the rest of this
section: a list of binary questions run against one screen before it ships. Each
line takes a yes or a no, and the record carries the name of the person who
answered.
Ten lettered sections cover values, ranges, status colour, trends, provenance,
escalation, motion and privacy, consent and language, accessibility and
governance. Twenty-two of the forty-seven items carry a `→` link to the page
that states the rule they enforce.

The form is a binary per line rather than a score, because a score reports an
aggregate and the identity of the failing item is what a reviewer needs. A "no"
is either fixed before the screen ships or recorded as an accepted exception with
a name and a reason against it, which is what section J requires at the close of
the run.

The page is formatted to survive printing, so a review can be run on paper away
from the build.
[Print and export](../accessibility/print-and-export.mdx) states what a printed
health surface still has to carry.

## The rule [#the-rule]

**No screen that displays, interprets or collects health information ships
without this checklist completed, dated and attributable to a person.**

### A. Values and numbers [#a-values-and-numbers]

* [ ] Every displayed value has its unit, visible, adjacent, and in the
  accessible name. → [Numbers, units and precision](./numbers-units-precision.mdx)
* [ ] Precision matches the source; no value is rendered from an unformatted
  float.
* [ ] The unit system is the reader's stored preference, and any conversion is
  labelled. → [Unit systems](./unit-systems.mdx)
* [ ] Zero, absent and failed states are distinguishable from each other and none
  renders as a number. → [Uncertainty, staleness and missing data](./uncertainty-and-staleness.mdx)
* [ ] Out-of-scale values are shown at magnitude, not clamped to the end of a
  gauge.
* [ ] Numbers that appear in a list or update in place use tabular figures.

### B. Ranges and interpretation [#b-ranges-and-interpretation]

* [ ] Any comparison against a range shows the range and names whose range it is.
  → [Reference ranges](./reference-ranges.mdx)
* [ ] The words *normal*, *abnormal*, *healthy*, *good*, *bad*, *poor* and
  *optimal* appear nowhere in a string about the reader's own result.
* [ ] Age, sex and other qualifiers are checked, and the range applies to this
  reader or no range is shown.
* [ ] No surface asserts a cause, a diagnosis or a prognosis.
* [ ] Any risk figure is absolute, with a population, a time window and a
  baseline. → [Risk and statistics](./risk-and-statistics.mdx)

### C. Status and colour [#c-status-and-colour]

* [ ] Status is one of the four levels, and its meaning matches the definition.
  → [Clinical status semantics](./clinical-status-semantics.mdx)
* [ ] Status is carried by colour **and** icon **and** word, and this is
  verified in greyscale.
* [ ] No element resolves a colour from both the category and the status
  namespace. → [The two colour axes](./two-colour-axes.mdx)
* [ ] Category colour does not change with the value.
* [ ] At most one `urgent` and at most two `attention` surfaces on the screen.
  → [Alarm fatigue](./alarm-fatigue.mdx)
* [ ] Non-clinical messages do not use the clinical status palette.

### D. Trends and time [#d-trends-and-time]

* [ ] Every value shows when it was measured, and staleness is visible without
  reading a caption.
* [ ] No trend is drawn below the metric's minimum window, and gaps are drawn as
  gaps. → [Trends and change](./trends-and-change.mdx)
* [ ] Direction and valence are distinguished; no coloured arrow asserts a
  judgement the product cannot support.
* [ ] Nothing is extrapolated or projected.

### E. Provenance [#e-provenance]

* [ ] Every value's provenance class is recorded and visible in plain words.
  → [Data provenance and device accuracy](./data-provenance-and-device-accuracy.mdx)
* [ ] No estimated or self-reported value drives a clinical status on its own.
* [ ] No manufacturer accuracy claim is restated as the product's own.

### F. Escalation, emergency and crisis [#f-escalation-emergency-and-crisis]

* [ ] The escalation ladder is respected: no rung skipped, no parallel escalation
  of the same fact.
* [ ] De-escalation is communicated when a value returns to `steady`.
* [ ] If this screen can produce an emergency finding, the emergency path is
  implemented in full and has been tested this release.
  → [Emergency and escalation](./emergency-and-escalation.mdx)
* [ ] If any question here can disclose risk of self-harm, the response pathway
  exists, is clinically reviewed, and always leads somewhere.
  → [Crisis and self-harm](./crisis-and-self-harm.mdx)
* [ ] Any result that may distress is announced before it is shown, and ends in a
  route to a person. → [Delivering difficult results](./delivering-difficult-results.mdx)

### G. Motion, notifications and privacy [#g-motion-notifications-and-privacy]

* [ ] No urgency is carried by motion, sound or haptics; nothing flashes; no
  clinical value animates on reveal. → [Motion in health UI](./motion-in-health-ui.mdx)
* [ ] Every animation has a defined reduced-motion behaviour that preserves
  information.
* [ ] Any notification for this screen carries no value or diagnosis and resolves
  to this screen. → [Notifications and off-screen alerts](./notifications-and-off-screen-alerts.mdx)
* [ ] Sensitive values mask without reflow, and the app obscures its content on
  backgrounding. → [On-screen privacy](./on-screen-privacy.mdx)

### H. Consent and language [#h-consent-and-language]

* [ ] Anything collected here has a stated purpose, a plain-language explanation
  and a revocation route where the data appears.
  → [Consent and disclosure](./consent-and-disclosure.mdx)
* [ ] Declining is possible, unpunished, and no harder than accepting.
* [ ] Every clinical term has a plain-English expansion in place.
* [ ] Copy has been read aloud, and read by someone who did not write it.

### I. Accessibility [#i-accessibility]

* [ ] Contrast meets the published floor in both themes, measured not assumed.
  → [Contrast conformance](../accessibility/contrast-conformance.mdx)
* [ ] Fully operable by keyboard, with visible focus and a sensible order.
* [ ] Screen-reader pass completed; status changes announced at the right
  politeness level. → [Screen readers](../accessibility/screen-readers.mdx)
* [ ] Legible and complete at 200% text with no clipped clinical sentence.
  → [Text resizing and zoom](../accessibility/text-resizing-and-zoom.mdx)
* [ ] Touch targets meet the floor with adequate separation.
* [ ] Screen is comprehensible in greyscale and under a colour-vision
  simulation. → [Colour independence](../accessibility/colour-independence.mdx)

### J. Governance [#j-governance]

* [ ] Every threshold on this screen traces to a written clinical rule with a
  named owner and a date.
* [ ] Any hazard identified during this review is in the hazard log.
  → [Regulatory context](./regulatory-context.mdx)
* [ ] Every "no" above is either fixed or recorded as an accepted exception with
  a name and a reason.
* [ ] Reviewer name and date recorded.

## Why (evidence) [#why-evidence]

<ResearchNote evidence="opinion" date="2026-09-02">
  **Opinion.** The checklist form is borrowed rather than invented, because
  checklists are standard practice in aviation and in surgery for the reason
  that applies here: expertise does not prevent omission under time pressure.
  We do not cite the surgical checklist literature because we are not claiming a
  comparable effect; we are claiming that a list of binaries is harder to skip
  than a page of principles.

  Two decisions about the list's form are worth defending. The first is binaries
  rather than scores, because a score of 88% hides which item failed and in this
  domain the identity of the failing item is the whole of the information. The
  second is that every item resting on a documented rule links to the page
  arguing for it, so a reviewer who disagrees can read the argument rather than
  only the rule, because a checklist whose items cannot be interrogated becomes
  ritual within two quarters. Twenty-two of the forty-seven items carry that
  link.

  The list runs to forty-seven items across ten sections and is meant to be
  completed in one sitting rather than spread across a sprint. We have not
  measured how long it takes in practice.

  What would change our mind: a review record showing that a shorter list is
  completed more honestly, in which the shortened list produces fewer items
  answered without a reason and no rise in defects found after ship. The first
  items to go would be the four this page already names as future lint rules, in
  sections A, B and C, once a tool checks them and the human question is
  redundant.
</ResearchNote>

## Applying it [#applying-it]

<DoDont>
  <DoDont.Do>
    Run it before the design review rather than after the build. The items in
    sections A through D resolve into layout, wording and precision decisions,
    and each of those is cheaper to change while the screen is still a design.
  </DoDont.Do>

  <DoDont.Dont>
    Run it as a release gate only. A review held at the point of release meets a
    team that can no longer absorb a layout or copy change, so the items that
    fail are signed off as exceptions rather than repaired.
  </DoDont.Dont>
</DoDont>

<DoDont>
  <DoDont.Do>
    Record exceptions with a name, a reason and a review date, in the same place
    as your hazard log.
  </DoDont.Do>

  <DoDont.Dont>
    Let an item be "not applicable" without saying why. An unexplained "not
    applicable" is indistinguishable in the record from an item nobody checked,
    which is why section J requires a name and a reason against every exception.
  </DoDont.Dont>
</DoDont>

<DoDont>
  <DoDont.Do>
    Have someone who did not build the screen run section H's read-aloud check.
    An author reads their own copy with the intended meaning already supplied,
    so a sentence that only parses for someone who knows the intent survives the
    author's reading and fails a first-time reader's.
  </DoDont.Do>

  <DoDont.Dont>
    Self-certify the whole list. An author answers each item against the screen
    they intended to build rather than against the one that renders. The items
    that depend on a reader who does not already know what the screen is meant
    to say are the ones an author cannot answer for.
  </DoDont.Dont>
</DoDont>

## Components that implement this [#components-that-implement-this]

{/* Generated from `implements`. Do not restate the list by hand. */}

Some of these items will eventually be machine-checkable: the banned-word list,
the two-axis rule, the presence of a unit, and the alert budget are all lint
rules waiting to be written. Until those rules exist, every item here is a
human question, answered by the reviewer whose name section J requires on the
record.

## What this does not cover [#what-this-does-not-cover]

* **Clinical review.** This list checks the interface, and it does not assess
  whether your organisation's thresholds, ranges or clinical content are
  correct. Passing it is not clinical sign-off.
* **Clinical risk management process.** Hazard logs, safety cases and the
  clinical safety officer role belong to your organisation and are covered by
  [Regulatory context](./regulatory-context.mdx).
* **Security review, privacy impact assessment and data protection assessment.**
  Each of the three is a separate exercise with its own record, and a completed
  copy of this list stands in for none of them.
* **Usability testing with real users.** This list checks whether a screen meets
  the rules, and testing checks whether a reader understood what the screen
  said, so neither result substitutes for the other.
* **Independent component-level accessibility review.** Every component in the
  catalogue has been audited against WCAG 2.2 AA by its own authors, in a static
  source pass and a rendered pass, and the findings were fixed in the same
  change. That work was run by the authors, so it is not an independent review,
  and no component has had a clinical review yet. Passing this screen checklist
  stands in for neither. See
  [The audit is author-run](../project/decisions/0025-the-audit-is-author-run.mdx).

## Updates to this page [#updates-to-this-page]

<Reviewed />
