Capabilities
A declarative rule engine for oncology recommendations. JSON profile in — Plan with two alternative tracks (standard + aggressive) out, every claim shipping with a citation to its source.
2026-06-11 11:51 UTC.
One JSON profile → two treatment plans, every claim cited.
No LLM in the clinical decision. No server. Patient JSON stays on the machine. Per-profile processing: 50-200 ms.
# example: DLBCL disease: DLBCL-NOS line_of_therapy: 1L biomarkers: CD79B_mut: true MYC_rearr: false COO: ABC findings: ipi: 3 ecog: 1 ldh_ratio: 1.8
- Resolve algorithm
- Flatten findings
- Eval 594 RedFlags
- Walk decision tree
- Materialize tracks
- Resolve regimens
revise_plan() refreshes the recommendation as new biomarkers or findings arrive.Coverage matrix
/diseases.html — per-disease table:
counts of biomarkers / drugs / indications / red flags, 1L+2L checkmarks, fill% and verified%.
Grouped by lymphoid + myeloid hematology and solid tumors. Canonical UI for
/disease_coverage.json.
→ View matrix
/gallery.html — 586 cases
(159 hand-curated + 362 verified variant profiles + 65 auto-base). Each — a full Plan or
Diagnostic Brief with all citations. Disease-grouped drill-down.
→ View examples
6-stage request processing
The clinician feeds the engine a JSON profile. The engine runs 6 sequential stages and returns a Plan with ≥2 tracks (CHARTER §15.2 C6 — alternative never hidden).
disease.id + line_of_therapy + disease_state → Algorithm entity.any_of/all_of/none_of with thresholds).CIViC actionability
Actionability data used to come from OncoKB — but their ToS blocked non-commercial public
derivatives (conflict with CHARTER §2). We migrated to CIViC (CC0) (WashU).
The engine reads a local nightly YAML snapshot
(knowledge_base/hosted/civic/<date>/) — deterministic + offline. The
fusion-aware matcher handles both point mutations (BRAF V600E) and fusions (BCR::ABL1).
Render: ESCAT tier — primary, CIViC evidence rating — in details. Monthly refresh CI opens
a PR with the diff.
Multi-phase regimens
Regimen.phases is an ordered list of named blocks (induction → consolidation →
maintenance), each with its own cycle schedule and transition trigger. Real-world protocols
(R-CHOP × 6 → maintenance, AML 7+3 → HiDAC consolidation, axi-cel bridging → infusion).
bridging_options — bridging regimens between phases (for CAR-T). Legacy
single-phase YAMLs auto-wrap into one-phase form via auto_wrap_legacy_components().
Citation guard — 3 layers
A three-layer verification system — three places where we catch unsourced claims:
| Layer | Where | What |
|---|---|---|
| L1 · Loader | YAML load (Pydantic) | SRC-* referential integrity. Unknown SRC-ID → load fail. |
| L2 · Verifier | Contributor PR (CI) | Three-layer grounding: source exists, paraphrase grounded, anchor in section. |
| L3 · Render | HTML output | No citation → warn-badge (lenient) or skip cell (strict_citation_guard=True). |
Three ways to run
python -m knowledge_base.engine.cli --patient profile.json --render plan.html.
Offline.
Python WASM + micropip + engine bundle (~2.4 MB). First load 8-15 s, then like a local CLI. Service worker for offline.
from knowledge_base.engine import generate_plan, revise_plan —
EHR / CSV / batch testing. Stateless, deterministic.
What the engine reads from a profile
Only structured fields with explicit semantics. Unknown fields are ignored — no hidden effects.
| Category | Fields | How we use it |
|---|---|---|
| Disease (entry point) | disease.id · icd_o_3_morphology · line_of_therapy · disease_state |
determines which Algorithm to run |
| Diagnostic mode | suspicion.lineage_hint · tissue_locations · presentation |
activates DiagnosticPlan instead of Plan |
| Demographics | age · ecog · fit_for_transplant · decompensated_cirrhosis · pregnancy_status |
filter on Indication.applicable_to.demographic_constraints |
| Biomarkers | any BIO-X from KB: BIO-CLL-HIGH-RISK-GENETICS, BIO-HCV-RNA, … |
trigger RedFlags, filter Indications, map to CIViC |
| Findings | hundreds of structured fields — dominant_nodal_mass_cm, ldh_ratio_to_uln, tp53_mutation, … |
thresholds in RedFlag triggers |
| Prior tests | prior_tests_completed: [TEST-IDs] |
excludes already-completed tests from workup_steps |
What the engine returns — Plan
| Field | Contents |
|---|---|
tracks[] | ≥2 alternative tracks (default first): indication + regimen (+ phases) + monitoring + supportive_care + contraindications |
access_matrix | per-track aggregate: registrations, reimbursement, cost ranges, AccessPathway. Stale-cost warning > 180 days. |
experimental_options | third track — enumerate_experimental_options() queries ClinicalTrials.gov v2. 7-day TTL cache. |
actionability_hits | CIViC matches with ESCAT-primary + variant, evidence rating, clinical significance, source citations |
fda_compliance | FDA Criterion 4 fields: intended_use, patient_population_match, algorithm_summary, automation_bias_warning |
trace | step-by-step walk_algorithm history: step / outcome / branch / fired_red_flags |
warnings | schema/ref errors, time_critical disqualifications, missing data hints, citation-guard warnings |
supersedes / superseded_by | version chain between plans for the same patient |
Optionally enabled: MDT brief — orchestrate_mdt() adds
required/recommended/optional roles from 16 virtual specialists + open questions + provenance graph.
Plan updates — revise_plan()
Three legal transitions + one forbidden. The previous plan is not mutated — a deep copy
is returned with superseded_by set. Per CHARTER §10.2, old versions persist indefinitely.
| From | Change | Transition | Result |
|---|---|---|---|
| DiagnosticPlan vN | suspicion only | diagnostic → diagnostic | DiagnosticPlan v(N+1) |
| DiagnosticPlan vN | histology confirmed | diagnostic → treatment (promotion) | Plan v1 |
| Plan vN | update with histology | treatment → treatment | Plan v(N+1) |
| Plan vN | histology removed | ILLEGAL — ValueError, CHARTER §15.2 C7 | |
Plan.knowledge_base_state.algorithm_version records the KB version →
same input + same KB = same output.
Open Questions — engine refuses to decide without data
Instead of a silent default, the engine explicitly flags which fields are missing and which test or report is needed (MDT_ORCHESTRATOR_SPEC §3). Rendered into the Plan as a separate section, never hidden.
| Code | Trigger | What the engine emits |
|---|---|---|
| Q1 | Histology not confirmed | «Treatment Plan generated against ICD-O-3 code only; confirm primary histology before therapy» |
| Q2 | Stage missing | «Lugano/Ann Arbor stage required for confident risk-stratification» |
| Q3 | RF clause incomplete | «Cytogenetic panel incomplete; high-risk status assessed with partial data» |
| Q4 | Biomarker missing | «IGHV mutation status + FISH del(17p) required to confirm 1L recommendation» |
| Q5 | Performance status missing | «ECOG required for transplant-eligibility assessment»; falls back to conservative default |
| Q6 | Drug not reimbursed | «D-VRd: daratumumab not currently reimbursed; verify funding pathway» |
| DQ1-4 | Diagnostic-mode: tissue / lineage / presentation / hypotheses missing | lowers workup match confidence; lineage_hint + tissue dominate |
Personalization gaps — deliberate
«Personalization» in OpenOnco is rule-based selection from a fixed catalog, not AI-generation. Deliberate architectural choice (CHARTER §8.3).
No per-patient dose calculation
Regimen stores the standard dose (bortezomib 1.3 mg/m²), not multiplied by patient BSA, not adjusted automatically for CrCl 30. By design — to avoid FDA device classification.
No response-adapted cycles
Regimen pins total_cycles: 6 + 2 maintenance. Not adapted automatically by response (PR vs CR after PET2). Re-staging via a separate revise_plan.
Genomic matching limited to CIViC
Outside CIViC (rare, novel variants) emits a warning «no actionability evidence», without creative interpretations.
SupportiveCare identical across cohort
PJP prophylaxis attached to D-VRd for everyone — the engine doesn't know alternatives (dapsone instead of bactrim). The clinician substitutes.
No cumulative-toxicity tracking
2L plan for a patient who got bortezomib in 1L with grade 2 neuropathy — the profile doesn't carry prior_treatment_history as a structured field; the clinician interprets manually.
CHARTER constraints — will not change
Principled architectural decisions that gate FDA / clinical safety.
LLM doesn't decide clinically
LLM only: boilerplate, doc drafts, extraction (with human verification), translation with clinical review. Not: regimen choice, doses, biomarker interpretation.
No histology — no Plan
Treatment Plan only if disease.id is confirmed. Otherwise DiagnosticPlan mode. revise_plan treatment → diagnostic is forbidden.
No time-critical
Not intended for emergency oncology (oncologic emergencies). Indication with time_critical: true → disqualification warning.
Two-reviewer merge
Any change to clinical content needs 2 of 3 Clinical Co-Lead approvals. Without that — STUB.
Anti automation-bias
Always ≥2 tracks side-by-side. Alternative is never buried, never «click to expand». The clinician sees a choice, not a directive.
Patient data never in repo
patient_plans/ gitignored. Site shows only synthetic examples. Telemetry forbidden without explicit consent.
Never list — what the engine never does
Hide the alternative track
Both recommendations are always shown. The UI has no «expand to see alternative» pattern.
Generate Indication via LLM
Everything from the curated KB. No matching Indication → warning, not «creative invention».
Modify doses
Doses come from the standard NCCN/ESMO. Adjustments only via explicit dose_modification_rules.
Judge «which is better»
The algorithm picks a default but doesn't claim it's superior. The clinician has full autonomy — automation_bias_warning.
Interpret imaging
«Bulky disease» arrives as a structured field, not from image analysis. Image analysis = device classification.
Cohort matching
«N patients chose X in M% of cases» — requires a persisted patient registry + privacy review. Not yet.
Current coverage — STUB status and gaps
| Category | State | Meaning |
|---|---|---|
| Diseases with full chain | 77 / 92 | The rest are partially modeled |
| Indications 1L | 230 | First line for all 92 diseases |
| Indications 2L+ | 172 | 34 heme + 21 solid. Other solid 2L+ partial. |
| Pediatric oncology | 0 | Out of MVP scope — separate track |
| Radiation therapy | partial | RT in multimodal Indications; not modeled as a standalone entity |
| Surgery | not modeled | Surgical oncology indications absent |
| Formulary live-feed | static flag | Hard-coded on regimens; not auto-refreshed |
| Reviewer dual sign-off | 15/806 | Primary bottleneck metric — capacity plan: 806 → ≥85% verified |
| Live gap dashboard | → link | Build-generated audit (sign-off, solid 2L+, surgery, RT, supportive care) |
Clinician workflow
The engine prepares for a tumor board, it does not replace one. The clinician:
1) verifies sources (every claim cited, guard already dropped uncited cells) →
2) closes Open Questions (orders tests, calls revise_plan) →
3) adapts to the patient (doses, supportive care, regional availability) →
4) discusses at the tumor board (MDT brief — structured agenda).
Engine: draft. Clinician: final.
TaskTorrent — verify algorithms with your AI's tokens
The biggest gap — 15/806 dual-reviewer sign-off. The bottleneck doesn't dissolve with new code — it dissolves through contributors with AI tools who pick up structured chunks of work.
A maintainer publishes a «chunk» — one self-contained task (~100k–300k tokens): «reverify BMA evidence for DLBCL × 17 entities» or «backfill source-license for 8 sources». A contributor claims the chunk, AI executes, PR opens.
An AI tool with a token budget (Claude Code Pro+, Codex, Cursor, ChatGPT with web). GitHub + gh CLI.
Python 3.10+. ~1-3 hours of your time. No clinical expertise required —
you trigger structured drafting; the clinical co-leads sign off.
Copy the 8-line prompt from
CONTRIBUTOR_QUICKSTART.md
into your AI agent. It finds an available chunk, claims it, runs the work, runs the validator, opens a PR.
The maintainer checks the mechanical part (validator, schema). A Clinical Co-Lead does a
sample review of the semantic part (CHARTER §6.1). Merge → sidecar landing → upsert into
hosted KB → render. Attribution recorded in _contribution_meta.yaml.
Recent activity
Over the last 4 days TaskTorrent saw 7 waves, dozens of chunks, ~73 BMA candidates, 23 BMA drafts ready for clinical signoff, 53 source stubs, 1,251 UA-language fields drafted. Recent structural updates:
- CIViC pivot — engine + 29 BIO + 399 BMA YAMLs migrated off OncoKB schema; monthly snapshot refresh CI.
- Multi-phase regimens (PR1-3) —
Regimen.phases,bridging_options, phases-aware render, back-compat auto-wrap. - Citation guard L3 — render-time check on every BMA cell.
- Coverage matrix — /diseases.html per-disease drill-down.
- 586 cases in gallery — Phase 2 chunked feat: ~60 new hand-curated in 4 days.