A synthetic cytogenetics/FISH Laboratory Information System (LIS) + interface simulator. It demonstrates SQL schema design, an order/specimen/result workflow, audit trails, outbound HL7/FHIR-style interface generation, inbound ORU-style ingestion with an interface error queue, validation logic, and pytest coverage.
This is an analyst-first, intentionally headless portfolio project. The reviewable artifacts are the data model, workflow rules, SQL, interfaces, validation evidence, and troubleshooting documentation.
Not affiliated with Epic Systems. This is a "Beaker-adjacent" learning project. It is not affiliated with, endorsed by, or connected to Epic Systems Corporation. It does not use Epic software, does not reproduce Epic build content or configuration, and contains no proprietary Epic material. "Beaker" is referenced only to describe the general category of laboratory information system this project models.
Data notice: All data in this project is synthetic. No PHI. No real patient data.
Authorship and AI assistance: Austin owns the project scope, laboratory domain decisions, architecture, requirements, safety boundaries, acceptance criteria, and release decisions. AI coding tools assisted with portions of implementation, tests, and documentation under those constraints. Automated checks and Austin's sign-off provide the project evidence; an AI self-review is not represented as independent review.
Verified state (this codebase): 164 passing pytest tests across eight suites, five end-to-end demonstration scenarios, and 41/41 requirements with result-level automated coverage that passes today, plus 18 manual UAT definitions. The v1.1 recovery UATs (UAT-011 - UAT-018, including both UAT-015 subcases) were manually executed and passed on 2026-07-25 - see the recovery UAT execution report. UAT-001 - UAT-010 remain defined but not manually executed. GitHub Actions verifies Python 3.11 and 3.12. CytoBridge v1.1 (controlled error-queue recovery) is complete.
v1 models exactly one panel - AML/MDS FISH - end to end, headless:
- Create a synthetic patient.
- Create an AML/MDS FISH order.
- Receive and accession a bone-marrow specimen.
- Enter structured, per-probe FISH results.
- Run validation.
- Block finalization if a required probe is missing.
- Record validation errors.
- Finalize the report when validation passes.
- Audit every important state change.
Session 2 adds outbound interface generation (see below): a finalized order
can now be exported as an HL7 ORU^R01-style message and a FHIR
DiagnosticReport-style JSON Bundle, both stored in the existing
interface_message table.
Session 3 adds inbound ingestion + the interface error queue (see below):
a synthetic instrument ORU-style message can now be parsed, matched to an open
order by accession number, and either filed as per-probe results or routed
to the interface_error_queue with a clear reason. Every inbound message is
stored in interface_message (direction = 'INBOUND').
Session 4 adds a validation & portfolio documentation package (no code changes): numbered requirements, a requirements-to-test traceability matrix, UAT scripts, a validation summary, risk assessment, known-issues, a change-control log, a demo script, a Mermaid workflow diagram, and a portfolio review. See Validation & portfolio docs.
flowchart LR
O["Order + specimen"] --> R["Per-probe results"]
R --> V{"Validation"}
V -->|"pass"| F["Final report + outbound messages"]
I["Inbound ORU-style message"] --> R
I -->|"invalid or unmatched"| Q["Interface error queue"]
The full lifecycle, inbound path, and audit events are documented in
docs/workflow-diagram.md.
A finalized AML/MDS FISH order can be rendered as outbound messages:
- HL7 ORU^R01-style pipe-delimited text (
MSH/PID/OBR/SPM/OBX). - FHIR R4-style
DiagnosticReportJSONBundle(Patient, Specimen, per-probe Observations, DiagnosticReport).
Both are generated from a single snapshot so they always agree, and both can be
stored in interface_message (direction = 'OUTBOUND') with no schema change.
These are educational, HL7/FHIR-style outputs - not certified or conformance-validated implementations. They use synthetic local codes and must not be sent to a production interface. All data is synthetic; no PHI.
Generation is finalized-only: exporting a non-finalized order (or one with
missing report/specimen/result data) raises interfaces.OutboundError rather
than emitting an incomplete message. The field-by-field mapping is documented in
docs/interface-mapping.md, with runnable samples
under sample_messages/outbound/.
An inbound, pipe-delimited ORU-style result message from a synthetic FISH
instrument (segments MSH/PID/OBR/SPM/OBX) is ingested by
interfaces.inbound_hl7.ingest_message:
- The raw message is always stored in
interface_message(direction = 'INBOUND'). - The
OBR-3accession number is matched to an existing, non-finalized order. - If it matches and every
OBXvalidates, the per-probe results are filed to the order (updating in place; oneINBOUND_RESULT_FILEDaudit event records the source message). - If it is invalid, unmatched, or not fileable, the whole message is routed to
interface_error_queue(status = 'OPEN') with a clear reason, and nothing is filed (all-or-nothing).
Messages route to the error queue when the accession is missing, does not match
an order, or the order is already finalized; when a required segment or all
OBX segments are absent; when a probe code is unknown for the AML/MDS panel;
when cells_abnormal exceeds cells_scored or a numeric field is malformed; or
when the specimen type is incompatible with the panel. The inbound mapping is in
docs/interface-mapping.md, an analyst runbook in
docs/interface-troubleshooting.md, and
runnable samples under sample_messages/inbound/.
This is an educational HL7-style parser - not a certified HL7 engine (no MLLP framing, ACKs, or conformance validation). Line endings are lenient. All data is synthetic; no PHI.
CytoBridge v1.1 adds a controlled recovery service so an analyst can safely
recover a failed inbound message that landed in interface_error_queue. It is a
small, headless Python service (src/recovery.py) with three
public functions - no UI, API, CLI, scheduler, or transport:
from src import recovery
recovery.retry_queue_item(conn, queue_id, *, request_id, actor)
recovery.redrive_queue_item(conn, queue_id, corrected_payload, *, request_id, actor)
recovery.get_recovery_history(conn, queue_id)Each failed queue item carries a structured failure_code, failure_category,
and recovery_policy that decide what is allowed:
- Unchanged retry (
RETRY_ORIGINAL) - for an OPENORDER_NOT_FOUNDitem, once the matching order exists. It reuses an exact copy of the immutable original payload on a new message. - Corrected re-drive (
REDRIVE_CORRECTED) - for an OPENREDRIVE_ONLYorRETRY_OR_REDRIVEitem. It processes a caller-supplied corrected payload on a new message. - Terminal rejection -
ORDER_FINALIZED/ORDER_CANCELLEDitems areTERMINAL; recovery is rejected and never reopens or unfinalizes an order.
Safety properties (each transcribed from the frozen design, and proven by tests):
- Immutability - the original failed message and the queue's
raw_payloadare never modified; only a new recovery message can reachFILED. - Idempotency -
request_idis globally unique. A matching replay returns the recorded outcome and writes nothing; a mismatched reuse is aREQUEST_ID_CONFLICT(audit-only, fabricates nothing); a queue item has at most oneSUCCEEDEDrecovery. - Attempt history - every attempt (
SUCCEEDED/FAILED/REJECTED) is recorded with apayload_sha256fingerprint and anoutcome_detail, and is retrievable in order. - Rollback - each operation commits or rolls back as a unit; a handled
failure preserves the attempted message as
ERROREDand the attempt asFAILED, rolls back all filing side effects, and leaves the queueOPENfor a later newrequest_id.
The recovery design, requirements, and test intent were approved and frozen by
Austin before implementation; they are pre-implementation decision records:
validation/v1.1-design-record.md,
validation/v1.1-requirements.md, and
validation/v1.1-test-intent.md. Current-state
implementation evidence lives in the
traceability matrix and
validation summary. The analyst runbook is
docs/interface-troubleshooting.md, the
recovery flow is diagrammed in
docs/workflow-diagram.md, scenario 5 of
python -m src.demo_run shows representative cases, and the reviewable synthetic
corpus is under sample_messages/recovery/.
The v1.1 recovery service is a synchronous, headless, educational implementation - not a production interface-engine recovery subsystem. All data is synthetic; no PHI.
- Python (standard library only at runtime).
- SQLite via the stdlib
sqlite3module. - Raw, hand-written SQL in
schema.sqlandqueries/. No ORM. - pytest for tests.
- Intentionally headless - analyst interaction is demonstrated through Python, raw SQL, generated messages, and validation artifacts.
schema.sql DDL: PK/FK/CHECK constraints + AML/MDS panel seed data
queries/ Analyst SQL (parameterized where appropriate)
turnaround_time.sql TAT for finalized orders (hours)
pending_review.sql Orders awaiting review, STAT first
stat_pending.sql Un-finalized STAT orders, aging
validation_error_rate.sql Share of orders with blocking errors
audit_lookup.sql Full audit trail for one order (bind :order_id)
interface_error_queue.sql Open inbound interface errors
src/
db.py sqlite3 helpers; parameterized queries; query loader
workflow.py patient/order/specimen/result/finalize + audit
validation.py validation rules (returns typed findings)
reports.py report summary + seam for a future ISCN parser
interfaces/ interface generation + ingestion
__init__.py collect_report_data + store_message + shared types
outbound_hl7.py HL7 ORU^R01-style message generation (Session 2)
outbound_fhir.py FHIR DiagnosticReport-style JSON Bundle (Session 2)
inbound_hl7.py inbound ORU-style ingestion + error queue (Session 3)
recovery.py controlled error-queue recovery service (v1.1)
demo_run.py happy path + missing-probe + outbound + inbound + recovery
sample_messages/
outbound/ sample generated HL7 + FHIR messages
inbound/ sample inbound instrument ORU-style messages
recovery/ synthetic recovery corpus (originals + corrected) (v1.1)
docs/
interface-mapping.md outbound + inbound field-by-field mapping
interface-troubleshooting.md analyst runbook + controlled recovery workflow
demo-script.md ~6-minute screen-share walkthrough
workflow-diagram.md Mermaid workflow + interface + recovery diagrams
portfolio-review.md what it proves / Epic boundary / resume
hiring-manager-review.md scorecard + resume/interview framing
validation/ validation package
requirements.md v1 numbered requirements (R-001...R-019)
v1.1-design-record.md frozen v1.1 design decision record (pre-impl)
v1.1-requirements.md frozen v1.1 requirements (R-020...R-041) (pre-impl)
v1.1-test-intent.md frozen v1.1 test intent (pre-impl)
traceability-matrix.md requirement -> code -> test -> UAT (R-001...R-041)
uat-test-scripts.md manual analyst UAT scripts (UAT-001...UAT-018)
v1.1-recovery-uat-execution-report.md recovery UAT execution evidence (UAT-011...UAT-018)
validation-summary.md approach + results summary
known-issues.md limitations and tracked issues
change-control-log.md per-session + v1.1 task change history
risk-assessment.md synthetic LIS/interface + recovery risks
tests/
test_workflow.py workflow lifecycle + audit + constraints
test_validation.py validation rules
test_outbound_interfaces.py outbound HL7/FHIR generation + export gating
test_inbound_interfaces.py inbound ingestion + error-queue routing
test_queries.py result assertions for analyst SQL views
test_recovery_schema.py v1.1 recovery schema + constraints
test_failure_classification.py v1.1 structured failure classification
test_recovery_service.py v1.1 controlled recovery service behavior
- Real constraints: primary keys, foreign keys (
PRAGMA foreign_keys = ON), andCHECKconstraints - e.g.cells_abnormal <= cells_scored, enumerated order/specimen/interpretation statuses, and a rejected specimen must carry a reason. - One result per probe per order (
UNIQUE (order_id, probe_id)); re-entering a probe updates the existing row. - Audit trail (
audit_event) records every state change with entity, action, actor, and detail. - Validation findings are typed (
ERRORblocks finalize;WARNINGis advisory) and persisted tovalidation_error.
| rule_code | severity | meaning |
|---|---|---|
SPEC_ACCESSIONED |
ERROR | Specimen must be received and accessioned (not rejected/missing) |
MISSING_PROBE |
ERROR | Every required probe must have a result |
ABN_EXCEEDS_SCORED |
ERROR | Abnormal cells cannot exceed scored cells |
INTERP_CONSISTENCY |
ERROR/WARN | Interpretation must agree with percent-abnormal vs probe cutoff |
CELL_COUNT_LOW |
WARNING | Scored-cell count below the minimum threshold |
The consistency check is cutoff-aware: a percent-abnormal at/above a probe's
abnormal_cutoff_percent that is still called NORMAL is a blocking error
(missed abnormal), while an ABNORMAL call below cutoff is an advisory warning.
From the repo root:
python -m src.demo_runThis runs five scenarios against a fresh in-memory database: a complete order
that passes validation and finalizes (with report summary and audit trail
printed); an order missing a required probe whose finalization is blocked
with the validation findings shown; outbound export of the finalized order to
HL7 ORU + FHIR DiagnosticReport messages stored in interface_message; inbound
ingestion - a valid instrument message filing probe results to an open order,
alongside unmatched/malformed messages landing in the interface error queue; and
controlled recovery of failed messages - a corrected re-drive, an unchanged
ORDER_NOT_FOUND retry, a handled failure followed by a later success, and
duplicate/replay/REQUEST_ID_CONFLICT protection, all through the public
recovery service.
pip install -r requirements-dev.txt # pytest only
python -m pytest -q # 164 testsGitHub Actions runs the full test suite and demonstration scenarios on Python 3.11 and 3.12 for every push and pull request.
CONTRIBUTING.mddefines development, validation, synthetic-data, and scope-control expectations.SECURITY.mddefines vulnerability reporting and data-safety response.LICENSEmakes the project available under the MIT License..github/dependabot.ymlschedules monthly Python and GitHub Actions dependency updates..github/pull_request_template.mdprovides a validation and safety checklist for proposed changes.
A documentation package demonstrating a validation mindset over Sessions 1-3 (no code changes):
- Validation package (
validation/): requirements / traceability matrix / UAT scripts / validation summary / known issues / change-control log / risk assessment - Walkthrough & review (
docs/): ~6-minute demo script / Mermaid workflow diagram / portfolio review (what it proves, the Epic/Beaker boundary, resume bullets, and interview talking points) / hiring-manager review (scorecard + resume/LinkedIn framing)
Every requirement (R-001-R-041) traces to the code (file/function or schema
constraint), result-level automated pytest coverage, and a manual UAT script
(UAT-001-UAT-018). Automated coverage passes today. The v1.1 recovery UATs
(UAT-011-UAT-018) were manually executed and passed - evidence in the
recovery UAT execution report -
while UAT-001-UAT-010 remain defined but not manually executed. This is
Beaker-adjacent learning, not Epic build experience - see
portfolio review.
Done:
- HL7 ORU-style outbound message generation (Session 2).
- FHIR
DiagnosticReportJSON generation (Session 2). - Inbound instrument ORU-style ingestion: file valid messages to open orders; route malformed/unmatched messages to the interface error queue with a clear reason (Session 3).
- Validation & portfolio documentation package: requirements, traceability matrix, UAT scripts, risk assessment, demo script, workflow diagram (Session 4).
- Repository CI and maintenance baseline: automated tests/demo, licensing, security policy, contribution guidance, Dependabot, and PR checklist.
- Controlled error-queue recovery (v1.1): headless retry / corrected re-drive / attempt history with original-message immutability, idempotency, transaction-safe rollback, and terminal rejection - designed and approved by Austin, implemented and validated under bounded tasks (P2-001, P3-001 - P3-005), with the recovery UATs (UAT-011 - UAT-018) manually executed and passed.
Next bounded enhancements:
- ISCN nomenclature parser (seam already present in
reports.py).
The headless interface is deliberate. A UI, production transport, additional panels, authentication, and clinical deployment remain outside this project's scope so the portfolio signal stays focused on LIS/interface analyst work.