Mirolens

Removing hand-keyed identifiers from a retinal screening imaging pipeline

Screening sites were typing patient identifiers into camera consoles and sending graded outcomes on as documents. We made capture pull from a worklist and exposed the outcomes over a FHIR API.

Industry
Healthcare
Published

Outcomes

What changed, and how we know

  1. 01

    Fields typed at the camera console before capture

    Six fewer typed fields

    Before
    Seven fields entered by hand
    After
    One selection from the worklist
    Window
    At cutover
    Method
    The shipped capture path walked through on each of the four camera models, counting the inputs a technician still has to supply by hand
  2. 02

    When an identity mismatch is detected

    Detection moved from days to the same visit

    Before
    At the reading session, days after the patient left
    After
    At the point of capture, while the patient is still at the site
    Window
    Measured across the first full screening cycle after rollout
    Method
    Quarantine timestamps from the ingestion agents compared against grading timestamps in the client's own records
  3. 03

    How a referring hospital obtains a grading outcome

    A pull replaces a wait

    Before
    Waiting on a document sent after the reading session
    After
    Retrieved from the FHIR endpoint as soon as the report is signed
    Window
    Measured over the eight weeks after the first hospital connected
    Method
    Request logs at the facade compared with the client's previous dispatch records
  4. 04

    Capture during a site connectivity outage

    Connectivity no longer stops a clinic

    Before
    Clinics paused or fell back to writing identifiers on paper
    After
    Capture continues against the spooled worklist and reconciles when the link returns
    Window
    Observed across outages in the eight weeks after cutover
    Method
    Agent spool records for each outage, checked against the sites' own clinic logs

Challenge

Mirolens operates the imaging pipeline behind a community eye-screening programme — fundus cameras in optometry practices and mobile units, feeding a central grading service staffed by accredited graders.

Every study was identified by hand at the camera console and moved to grading as a file transfer, so transposed or mismatched identifiers were only discovered days later at the reading session, and referring hospitals received outcomes as documents that no downstream system could read.

Constraints

  • Screening clinics run to a booked list during working hours; there was no window in which capture could stop
  • Image data had to remain in the contracted region, including for any third party in the path
  • Changes to a clinical path required a hazard log and clinical safety sign-off before release
  • Four camera models from three vendors were already in the field and could not be replaced
  • Every view of an image had to be attributable to a named user and retained for the audit trail

Approach

Team
Two senior backend engineers and one integration engineer with DICOM experience, with a tech lead at 70 percent because every sprint closed on a hazard review, working alongside the client's clinical safety officer
Engagement model
Fixed-scope sprint
Duration
Thirteen weeks across three sprints, plus two weeks of shadow running before cutover

Three fixed-scope sprints, each with an acceptance list agreed in writing before it started and a hazard review at the end attended by the client's clinical safety officer.

Architecture

A store-and-forward agent at each screening site receives DICOM directly from the cameras and serves them a modality worklist built from that day's booked list, so identifiers are selected rather than typed. A FHIR facade in front of the grading service exposes referrals, imaging studies and signed reports to hospital systems, handing back references to images rather than the images themselves.

  • Go
  • TypeScript
  • PostgreSQL
  • SQLite
  • DICOM
  • HL7 FHIR
  • OpenTelemetry

Key decisions

Serve a modality worklist at the site rather than reconciling identifiers after upload
Identity errors were being created at the console, by typing. Reconciling afterwards can only detect them; taking the booked list to the camera stops them being made.
Store and forward at the site, spooling to local disk
Practice broadband drops, and the clinic still has a booked list to get through. Capture had to keep working while the link did not, and settle up when it returned.
Quarantine an unmatched study for a named person to resolve, rather than matching on best guess
A study attached to the wrong patient is a clinical safety hazard; a study held for an hour is rework. The system is built to prefer the second failure.
The FHIR facade returns image references, never pixel data
This keeps images inside the contracted region and out of any caller's infrastructure, and routes every view through one gateway where it can be attributed and logged.
Append-only audit records written before the image is served, in a store separate from the application database
An audit record written after the fact is one that can be lost on the failure path. Writing first makes an unlogged view impossible rather than unlikely.

The scope was fixed because the safety case had to be

A clinical safety case is written against a described system. Every time the description changes, the hazard log is reopened and someone accountable signs it again. That is the honest reason this ran as fixed-scope sprints rather than a rolling backlog: an open scope would have meant a hazard log that was never finished, and nothing could ship until it was.

The lock also forced the decision we would defend hardest. Where the agent cannot match a study to the worklist with certainty, it quarantines the study and asks a named person at the site to resolve it. Best-guess matching would have cleared that queue faster and would occasionally have attached an image to the wrong patient. We organised the system around the failure we could live with, and wrote that preference into the hazard log rather than leaving it buried in a matching function where the next engineer could quietly tune it away.

What we would do differently

We built the ingestion agent against a DICOM simulator and did not put it in front of the real cameras until the third sprint. One vendor's console truncated the accession number it echoed back, which broke matching on that model and consumed most of the sprint's contingency. Hardware in the field does not behave like a toolkit's test fixtures. We should have arranged access to one of each camera model in week one rather than week nine, even at the cost of a slower start.

Similar problem?

If you are working on something like this in healthcare, tell us what you are measuring today and we will tell you whether we can move it.