A language for clinical knowledge

Clinical phenotypes,
thoughtfully modelled.

Turn patient observations into meaningful clinical descriptions. Picorules gives you a small, expressive language to define, compose, and reuse clinical knowledge.

Open source. Built from clinical practice.

From evidence to meaningFig. 01
Composing a clinical phenotypeThree reusable clinical constructs — measurement summaries, patterns over time, and related conditions — contribute to a clinical phenotype.MeasurementsPatterns over timeRelated conditionsA clinicalphenotype
Reusable constructs. A richer clinical description.Illustrative composition
Observations → Constructs → Phenotypes

Small definitions. Shared understanding.

01 — The approach

Clinical knowledge,
made explicit.

A phenotype is more than a label. It is a description of a patient, built from evidence and the definitions that give it meaning.

Picorules makes those definitions visible: the observations they use, the history they consider, and the smaller constructs they bring together.

See how a ruleblock works
  1. 01

    Start with the evidence.

    Work with dated observations: measurements, diagnoses, and medications. Make explicit which facts matter, and when.

  2. 02

    Give each construct a definition.

    Express a measurement summary, a temporal pattern, or a classification as a named, reusable piece of clinical knowledge.

  3. 03

    Compose a richer picture.

    Bring those constructs together into a phenotype definition. Reuse its characteristics in registries, research, and clinical applications.

02 — The language

A small definition.
A clearer intention.

Picorules began by making clinical calculations easier to maintain in a database. Its compact definitions now also run in JavaScript, while keeping the clinical question in view.

SQLRetrieval equivalent
WITH egfr_history AS (
  SELECT eid, val,
    MIN(val::numeric) OVER (
      PARTITION BY eid
    ) AS egfr_lowest,
    ROW_NUMBER() OVER (
      PARTITION BY eid ORDER BY dt DESC
    ) AS observation_order
  FROM eadv
  WHERE att = 'lab_bld_egfr'
)
SELECT eid,
  val AS egfr_latest,
  egfr_lowest
FROM egfr_history
WHERE observation_order = 1;
Picorulesrenal_measurements.prb · excerpt
// Summarise a patient's recorded measurements
egfr_latest => eadv.lab_bld_egfr.val.last();
egfr_lowest => eadv.lab_bld_egfr.val.min();

Two questions, made explicit.

What is the latest recorded eGFR? What is the lowest? These summaries are reusable constructs that can contribute to a larger phenotype definition.

Get the complete ruleblock

An observation-summary example, with no disease classification. The SQL shows the retrieval operation; the compiler also supplies the surrounding ruleblock and output structure.

Inspect the actual compiler output PostgreSQL
--================================================
-- SQL Dialect: PostgreSQL
-- Ruleblock: renal_measurements
--================================================
CREATE TABLE ROUT_RENAL_MEASUREMENTS AS
WITH
  UEADV AS (
    SELECT DISTINCT eid FROM eadv
  ),
SQ_EGFR_LATEST AS (
    SELECT eid, val AS egfr_latest
    FROM (
      SELECT eid, val,
             ROW_NUMBER() OVER (PARTITION BY eid ORDER BY dt DESC) AS rn
      FROM eadv
      WHERE att = 'lab_bld_egfr'

    ) ranked
    WHERE rn = 1
  ),
SQ_EGFR_LOWEST AS (
    SELECT eid, MIN(val::numeric) AS egfr_lowest
    FROM eadv
    WHERE att = 'lab_bld_egfr'

    GROUP BY eid
  ),
SQ_RENAL_MEASUREMENTS AS (
    SELECT eid,
           CASE
           WHEN egfr_latest IS NOT NULL THEN 1
           ELSE 0
           END AS renal_measurements
    FROM UEADV
    LEFT JOIN SQ_EGFR_LATEST USING (eid)
    LEFT JOIN SQ_EGFR_LOWEST USING (eid)
  )
SELECT eid,
       egfr_latest,
       egfr_lowest,
       renal_measurements
FROM UEADV
LEFT JOIN SQ_EGFR_LATEST USING (eid)
LEFT JOIN SQ_EGFR_LOWEST USING (eid)
LEFT JOIN SQ_RENAL_MEASUREMENTS USING (eid)
-- ---
Download the SQL

History, made usable.

Select the latest result, summarise an interval, or follow change over time. Work with dated observations through the EADV model.

Explore the functions →

Definitions that compose.

Reuse named outputs from other ruleblocks. Build richer clinical descriptions from smaller, explicit constructs.

Explore composition →

Reasoning beside the rule.

Keep descriptions, citation keys, and output labels alongside the definition with documentation and attribute directives.

Explore documentation →

03 — Across platforms

One definition.
Many settings.

Keep your clinical definitions together while adapters handle the data source. Bring shared knowledge to a population registry or an individual patient application.

How the definitions connect to your data

Fetch what the rule needs.

The FHIR adapter inspects ruleblocks to identify their data requirements and build targeted queries. Terminology mappings connect attributes to the source's codes.

Read about smart fetching →

Fit into the clinical workflow.

The same requirements can generate CDS Hooks prefetch templates. SMART on FHIR applications can use the JavaScript evaluator after retrieving their records.

Explore FHIR integration →

Keep the mappings explicit.

The openEHR adapter uses AQL to retrieve observations. Each deployment supplies the coding, units, and dated values its definitions expect.

Explore openEHR integration →

04 — The npm packages

Build it into
your application.

Bring your clinical definitions into the software you already build. Start with the compiler, then add adapters and tools as you need them.

Explore the SDK

The compiler & runtime

picorules-compiler-js-core

A JavaScript and TypeScript package for parsing ruleblocks, generating SQL, and evaluating definitions directly in your application.

npm install picorules-compiler-js-core

Adapters & tools, also on npm

Try the JavaScript runtime In your browser

The same ruleblock can run against records in JavaScript. This example uses two synthetic observations, with no database connection.

import { parse, evaluate, EadvDataAdapter }
  from 'picorules-compiler-js-core';

// Serve the example ruleblock with your application.
const source = await fetch('/examples/renal_measurements.prb')
  .then((response) => response.text());

const [rule] = parse([
  { name: 'renal_measurements', text: source, isActive: true }
]);

// Synthetic records for one patient.
const adapter = new EadvDataAdapter([
  { eid: 1, att: 'lab_bld_egfr', dt: '2025-01-15', val: 44 },
  { eid: 1, att: 'lab_bld_egfr', dt: '2025-06-01', val: 52 }
]);

console.log(evaluate(rule, adapter));
// { egfr_latest: 52, egfr_lowest: 44, renal_measurements: 1 }

Latest: 52 · Lowest: 44

05 — Picorules Studio

Room to write.
Space to explore.

A browser-based workspace for developing ruleblocks. Write a definition, inspect its compiled SQL, and explore the results with synthetic data.

  • Syntax highlighting and a Monaco editor
  • SQL compilation for three database dialects
  • Synthetic data generation and PostgreSQL execution
Studio source code ↗
A ruleblock in contextIllustrative workspace
WriteCompileExplore

renal_measurements.prb

egfr_latest => eadv.lab_bld_egfr.val.last();
egfr_lowest => eadv.lab_bld_egfr.val.min();
Example output · synthetic observations
CharacteristicValue
Latest recorded eGFR52
Lowest recorded eGFR44

From clinical practice

An idea with
roots in care.

Picorules began in 2019 within Territory Kidney Care, where clinical calculations needed to run inside an Oracle database. The TypeScript compiler extends that foundation to other SQL databases and direct evaluation in JavaScript.

Origins, evidence, and open development

Published TKC studies evaluate specific clinical algorithms. Compiler tests and adapter comparisons examine software behaviour and selected outputs. These are different kinds of evidence, which the project describes separately.

Begin with a definition

Give clinical knowledge
a language.

Learn the ideas behind Picorules, or open the Studio and start exploring.

From an observation
to a construct.
From a construct
to a clearer picture.