clinlat — Clinical Lattice Types
A Rust substrate for symbolic clinical decision-making based on refinable hypothesis lattices and sound deduction operators.
Version: 0.3.0 Status: M2 milestone complete (Constrained refinement proposer)
Overview
The clinlat crate provides:
Hyp: A poset (partially ordered set) of clinical hypotheses, ordered by refinement (specificity).Outcome<H, A>: A result type for operator outputs (refined hypothesis or abstention).- Deduction operators: Sound functions that refine hypotheses using clinical evidence (e.g., SOFA-3 respiratory scoring).
This implements the patient-state substrate from Substrate-First Clinical AI, a position arguing that clinical AI safety must be enforced by symbolic reasoning, not learned components alone.
Quick Start
Creating a Hypothesis
use ;
// The top element (most general hypothesis).
let unknown = unknown;
// A specific hypothesis. Atoms are resolved through ontology adapters
// (SNOMED CT, RxNorm, LOINC, ICD-11); see `clinlat::ontology`.
let hypoxemia = Atom ;
let diagnosis = new;
Refinement Ordering
use ;
use Ordering;
let unknown = unknown;
let specific = new;
// `specific` refines (is more specific than) `unknown`.
assert_eq!;
Evidence with Provenance
use BTreeMap;
use Utc;
use ;
let observations = vec!;
let provenance = new;
let evidence = new;
SOFA-3 Respiratory Operator
use ;
# use BTreeMap;
# use Utc;
# use ;
# let evidence = new;
let operator = default_v0_2;
let outcome = operator.apply;
match outcome
Lower-level score_from_ratio(ratio, on_mech_vent) -> Option<u8> is also exposed
as a standalone numeric helper (no Evidence/Provenance plumbing) for callers that
just need the SOFA mapping in isolation.
KDIGO AKI Staging Operator
use ;
# use BTreeMap;
# use Utc;
# use ;
# let evidence = new;
let operator = new;
let outcome = operator.apply;
match outcome
The KDIGO AKI operator stratifies kidney injury severity by serum creatinine fold-change and urine output decline, per the Kidney Disease: Improving Global Outcomes 2021 guideline. Stage assignments (0–3) drive admission and dialysis decisions in critical care.
See clinlat/docs/operators/kdigo_aki_soundness.md for soundness argument.
Wells PE Risk Stratification Operator
use ;
# use BTreeMap;
# use Utc;
# use ;
# let evidence = new;
let operator = new;
let outcome = operator.apply;
match outcome
The Wells PE operator implements cumulative clinical scoring for pulmonary embolism risk, per Wells et al. (1997/2006). The gestalt assessment ("is PE your leading diagnosis?") is mandatory—the operator enforces clinician judgment as a core input, not an optional feature. Scores ≤4 → D-dimer testing (rule-out); scores >4 → CTPA imaging (rule-in).
See clinlat/docs/operators/wells_pe_soundness.md for soundness argument.
CURB-65 CAP Disposition Operator
use ;
# use BTreeMap;
# use Utc;
# use ;
# let evidence = new;
let operator = new;
let outcome = operator.apply;
match outcome
The CURB-65 operator stratifies community-acquired pneumonia (CAP) severity for disposition decisions, per the British Thoracic Society and IDSA/ATS guidelines. Scores 0–1 → outpatient; score 2 → ward admission; scores 3–5 → ICU evaluation with possible critical care.
See clinlat/docs/operators/curb65_soundness.md for soundness argument.
Constrained Refinement Proposer (M2)
A [RefinementProposer] is a black-box that suggests candidate refinements —
it never decides on its own; every candidate must still pass the ontology
gates ([ProposerConstraint], DEF-PS-15) and the soundness-verification gate
(propose_verify, the Diagram 3 SV node) before it can be used. This is
what lets clinlat plug in an untrusted or even hallucinating proposer (see
the LlmProposer below) without weakening the
substrate's safety guarantee.
use ;
# use BTreeMap;
# use Utc;
# use ;
# let evidence = new;
// Independent OperatorSet instances — one drives the proposer's search space,
// one drives the soundness gate — per this repo's test/usage discipline that
// shared-instance construction can mask non-determinism (see CLAUDE.md).
let search_operators = new.register;
let gate_operators = new.register;
let proposer = new;
match propose_verify
LatticeSearchProposer is trivially sound by construction (exhaustive search
over one-operator-step reachable hypotheses). See
docs/examples/example_sofa_kdigo_proposer.md
for a worked SOFA + KDIGO example.
LLM-Class Proposer (M2)
LlmProposer wraps a foundation-model API call (or an offline mock, for
CI/testing) behind the same RefinementProposer interface. The LLM can
hallucinate freely — invalid responses are filtered by ProposerConstraint
and logged as filtered candidates — while the substrate's refinement
behavior stays identical to a run through LatticeSearchProposer on the
same evidence. See
docs/examples/example_llm_proposer_sepsis.md
(hallucination filtered, valid candidate accepted) and
docs/examples/example_substrate_invariance_sepsis.md
(side-by-side proposer swap showing identical substrate output).
Proposer safety is discharged in two documents:
docs/invariants/inv-ps-06-proposer-safety.md— proves no proposer output can bypassOperatorSet::apply_set().docs/obligations/obl-ps-05-proposer-constraint.md— OBL-PS-05 discharge at property-test tier across both reference proposers.
Architecture
Hypothesis Lattice
Hypotheses are ordered by refinement:
Unknown (top)
/ | \
Score0 Score1 ... Score4 (bottom elements, incomparable to each other)
In this lattice:
- Unknown is the greatest element (least specific; no information).
- Score{N} elements are minimal (each is a specific diagnostic score).
- The partial meet of two elements is their greatest lower bound (if it exists).
Operators
An Operator is a function from a hypothesis and evidence to an outcome:
apply: (Hyp, Evidence) → Outcome<Hyp, AbstainReason>
The operator either:
- Refines the hypothesis to a more specific one based on evidence.
- Abstains with a reason (e.g., insufficient evidence, precondition unmet).
SOFA-3 Respiratory Component
The SOFA-3 operator scores respiratory dysfunction severity via the PaO₂/FiO₂ ratio:
| Ratio (mmHg) | Score | Meaning |
|---|---|---|
| ≥ 400 | 0 | No dysfunction |
| 300–399 | 1 | Mild |
| 200–299 | 2 | Moderate |
| 100–199 | 3 | Severe (requires ventilation) |
| < 100 | 4 | Very severe (requires ventilation) |
Per Sepsis-3, scores 3 and 4 are only valid for intubated patients; the operator abstains if this precondition is unmet.
Soundness
Each operator carries a soundness argument establishing three properties per DEF-PS-08 (Soundness of a deduction operator):
- Refinement monotonicity (INV-PS-03): If h₁ ⊑ h₂, operator output on h₁ refines that on h₂.
- No spurious refinement: Output never exceeds what the evidence justifies.
- Abstention purity (INV-PS-04): Abstention is structural, not error handling.
Soundness arguments are provided for all M1 operators:
- SOFA-3 respiratory:
docs/operators/sofa_resp_soundness.md(property-test tier: 46 tests) - KDIGO AKI:
docs/operators/kdigo_aki_soundness.md(informal-argument tier) - Wells PE:
docs/operators/wells_pe_soundness.md(informal-argument tier) - CURB-65 CAP:
docs/operators/curb65_soundness.md(informal-argument tier)
All soundness arguments discharge OBL-PS-03 (Operator set soundness) and satisfy INV-PS-01–INV-PS-06 (patient-substrate invariants).
Formal reference: See SPEC.md §2 (Patient-state substrate) and SPEC.md §8 (Bidirectional traceability) for the complete formalization of operator soundness and the mapping from principles (NOTE.md §4A) to formal definitions.
Status
M1 (Patient substrate completion, v0.2.0, shipped 2026-05-31): all eleven
4A-anchored SPEC.md elements (DEF-PS-01..15, INV-PS-01..06, OBL-PS-01..05)
reachable from running code; four operators discharged (SOFA at
property-test tier, KDIGO/Wells/CURB-65 at informal-argument tier).
M2 (Constrained refinement proposer, v0.3.0) — complete:
RefinementProposer/ProposerConstraint(Phase 8): black-box proposer interface (DEF-PS-14) with input- and output-side ontology gates (DEF-PS-15);propose_and_filterandpropose_verifyadapters wire proposer output through the soundness-verification gate with structured abstention (AbstainReason::NoOperatorLicenses); INV-PS-06 enforced by a dedicated structural test, not argument alone.LatticeSearchProposer(Phase 9): exhaustive one-operator-step search, trivially sound by construction; ~27 property cases for completeness, minimality, and monotonicity.LlmProposer(Phase 10): foundation-model adapter (with offline mock mode for CI) demonstrating the substrate-first claim — hallucinated candidates are filtered, valid candidates pass through, and system behavior stays sound either way.- OBL-PS-05 discharge + substrate-invariance (Phase 11): property-test
tier discharge across both proposers, plus a ≥10-case paired test proving
identical post-soundness-gate refinement across a proposer swap for the
same evidence — see
docs/obligations/obl-ps-05-proposer-constraint.md.
See the Constrained Refinement Proposer
section above for usage, and CHANGELOG.md for the full [0.3.0] entry.
Test coverage: 299 tests passing (all operators, both proposers, all phases).
References
- Position note: Substrate-First Clinical AI (NOTE.md) § 4A–4D (eighteen load-bearing principles).
- Formalization: SPEC.md § 2 (patient-state substrate definitions and proof obligations).
- Clinical references:
- Vincent JL, et al. The SOFA (Sepsis-related Organ Failure Assessment) score to describe organ dysfunction/failure. Intensive Care Medicine. 1996;22(7):707–710.
- Singer M, et al. The Third International Consensus Definitions for Sepsis and Septic Shock (Sepsis-3). JAMA. 2016;315(8):801–810.
Testing
All public types and functions have unit tests:
Documentation tests are included in rustdoc:
Building Documentation
Generate full API documentation with cross-references:
License
Code: MIT OR Apache-2.0 (see LICENSE-MIT and LICENSE-APACHE at repo root).
Contributing
Contributions follow the repository contribution order (see CONTRIBUTING.md):
- Show the synthesis is already published (and was missed in prior art).
- Demonstrate one of the eighteen principles is wrong or unnecessary.
- Show the substrate framing fails on a clinical decision not covered by the worked examples.
For questions or issues, see the main repository: https://github.com/SHA888/SFClinAI