simpledoctor · How it works

How it works

A clinical document goes in. Records a clinician could defend come out. Each stage either proves what it passes on or holds it back for a person.

The path of one document

Every stage below has exactly one module that owns its rule. When two modules answered the same question, they eventually disagreed, so the second answer was deleted rather than kept in sync.

  1. Source

    A textbook PDF, a journal article or a question bank

    A corpus recipe says where a document's bytes live and whether its apparatus pages (bibliography, index) were removed first. Removing them is a new corpus, because every content-addressed id changes with the bytes.

    recipesingest
  2. Page reader

    One walk over the pages, three streams

    Prose, figures and tables, and navigation apparatus. Fonts, private-use glyphs, columns and page furniture are repaired first, on evidence gathered from the whole document.

    page_readerfont_offsetsymbol_glyphsligaturespage_columnsreference_matter
  3. Passages

    Cut at sentences, never through a table row

    An oversized section is split by topic. The model picks boundaries between numbered sentences and never writes text; a split that does not cover every sentence once, in order, is refused.

    sentencespassagessemantic_chunking
  4. Layer 2 · Link

    Mentions become UMLS concepts

    Linked against a 792,516-concept clinical subset. Two names for one thing are collapsed first; a true tie goes to a cross-encoder or is quarantined. Abbreviations are expanded only from ranked evidence.

    linker_hookconcept_identitytie_breakabbreviations
  5. Layer 2 · Assert

    Negated, historical, hypothetical, or someone else's

    Each mention is read for negation, time and experiencer. A relative's finding travels on its own field and never reaches the patient's.

    assertionkinship
  6. Layer 2 · Decide

    Three verdicts, all counted

    Admitted, rejected or quarantined. A candidate missing from the totals is a defect.

    admissiondecisions
  7. Layer 3 · Curate

    A unit carries affirmed concepts only

    No field of a generation brief can carry a negated, hypothetical or historical concept, so a generator cannot be told one.

    chunkingunitsGenerationBrief
  8. Layer 3 · Generate

    Triplets, hard negatives, reasoning items

    Hard negatives are written in four clinical failure modes. Reasoning items are cases and questions with cited, step-by-step rationales.

    triplet_generatorreasoning
  9. Layer 3 · Audit

    A judge must confirm the flaw

    A negative is published only when a judge names what is wrong with it. An unreachable judge quarantines; it never approves.

    auditllm
  10. Stores

    Graph, vectors, datasets, ledgers

    Every model call is recorded in append-only ledgers keyed by a fingerprint of model, prompt version and settings. Stage gates decide whether an artifact may be spent on.

    graphvectorsoutcomespreferencegates

Reading the page

PDFs lose text in ways no byte-level rule can see. A broken font map yields printable characters, so the only evidence is whether the result reads as language. Each repair is proved on the whole document and applied span by span, and a span the proof does not cover is left exactly as printed.

A font that decodes to the wrong code point
One book's box and figure labels. A font counts as offset only when its text is unreadable as printed and readable at one constant shift over at least a hundred tokens: broken faces then read 0.125 function words against 0.000–0.003 for healthy ones. In that book the broken family touched 459 of 1,825 pages. Repair is per span, because the same font name also sets correctly encoded text: a per-token rule would have turned "TP21" into "qmON".
A ligature glyph read from the document's own words
The word is an illustration of the rule. In one book, 15,851 of 15,932 private-use glyphs sit beside a letter, and the fonts name the glyph only by its code point. A reading is admitted only from a closed ligature alphabet and only where it makes a word the book prints unbroken elsewhere. A capital belongs at the start of a word, or "Th" proved from "The" rewrites "oering" as "oThering". One book is refused by name: its glyphs are comparison operators, and a wrong reading would invert a clinical threshold.

The same page-level discipline removes furniture. "Investigations" is a real heading that recurs 301 times in one book and never twice at the same height; a watermark sits at the same coordinate on 151 of 218 pages. A line stamped at one fixed position on most pages is furniture wherever it appears. Before that rule, one book's licence footer rode along in 289 of its 370 passages; after it, none.

Reading the sentence

A negated finding must never become a positive assertion. The two defects below passed every self-authored test and were found only by independently labelled clinical notes.

Negation counted instead of scoped
Denies fever. No headache, no neck pain, no chest pain, no cough, no dysuria.
Before: cues counted
fever affirmed
headache affirmed
neck pain affirmed
chest pain affirmed
cough affirmed
dysuria affirmed
After: each cue scoped
fever negated
headache negated
neck pain negated
chest pain negated
cough negated
dysuria negated
Constructed illustration of the defect. A double-negation rule counted cues, and an even count cancelled to affirmed, so every finding in an enumerated review of systems flipped to positive.
Negation that crossed a section heading
COMPLICATIONS: None
POSTOPERATIVE DIAGNOSIS: 1) Colitis
Before
colitis negated
After
colitis affirmed
From the held-out corpus. "None" belongs to its own section; a sentence reader shared by every module now ends scope where the section ends.
A relative's finding is not the patient's
Mother had breast cancer at 45. An aunt had ovarian cancer. Patient denies breast lumps.
Patient findings
breast lumps negated
Family history, never the patient
breast cancer mother · 1st degree
ovarian cancer aunt · side unknown
Constructed illustration. The note never says which side the aunt is on, so lineage stays unknown rather than guessed. A family finding is never stored as the patient's history: "historical" means the patient's own past.

Deciding

Every candidate ends in one of three verdicts. The rule separating them is whether more information would change the answer, and each reason in the code declares its own verdict, so a new gate cannot pick one by how the failure feels.

Admitted

Cleared every gate: a real concept in the clinical subset, an in-scope semantic type taken from the ontology, a clear assertion.

Rejected

Wrong by definition, so a reviewer adds nothing. A code that matches the format but names nothing (C9999999), an out-of-scope type, dosing notation, a superseded span, a confidently low score. Counted, never queued.

Quarantined

The engine could not decide and a person could. Two concepts tied for one name, an abbreviation with several readings at the same rank of evidence, a relative's finding on a path that has no family field. Kept in full and queued for review.

Reviewer answers

A reviewer's answer re-enters admission as a proposal, so it cannot overrule the ontology. How far the answer travels comes from the reason, never from the reviewer.

Writing hard negatives

A retrieval model learns what a good passage is partly from passages that look right and are clinically wrong. Each hard negative is a rewrite of a real passage into one of four ways medicine goes wrong. A judge must name the flaw before the negative is published; if the rewrite turns out to be accurate, it is discarded.

Contraindication trap

Advises something the patient's situation rules out, or drops the exception. One generated example told clinicians no adjustment was needed in a patient on therapeutic warfarin.

Outdated practice

States what used to be standard care and no longer is.

Near-miss entity

Swaps in a closely related drug, test or condition that makes the statement wrong.

Unwarranted specificity

Asserts a precise value, subtype or diagnosis the evidence does not support. A swapped number is judged the same way.

Questions from published banks carry their own wrong options. Those are used as the author wrote them, with no failure mode assigned, because the source never said how each option is wrong.

Reasoning items and what their refusals are worth

A reasoning item is a case or a question whose rationale cites passages step by step. It is generated from a whole concept subtree of a book rather than one passage, and it must pass three layers in order. Each refusal records which layer refused it.

30 judged items from one chapter, layer by layer
30 items were generated. 27 passed structure, 3 refused. 20 passed citation anchoring, 7 refused. The judge accepted 6, refused 13 and could not be reached for 1. Generated 30 Passed structure 27 · 3 refused Passed citation anchoring 20 · 7 refused Accepted by the judge 6 · 13 refused · 1 quarantined
Measured 7 September 2026 on three generator arms. Read as yield, 6 of 30 looks like failure. Read as data, the 23 refusals are labelled negative examples on the same concepts as accepted ones.

So each refusal becomes the rejected half of a preference pair whose chosen half is an accepted item on the same concept and item kind. A quarantined item takes no side, because an outage is not a clinical flaw. A rejected item enters at most one pair: six accepted items against 23 refused would otherwise cross-multiply into 138 rows, of which 23 carry information. An item judged both ways across re-runs is refused rather than arbitrated.

Where it ends up

A store cannot forbid a careless query, so the safety lives in the names. In the graph, an assertion is the edge type rather than a property, so no query returns a denial without naming one. In the vector collection, three fields answer three different questions.

FieldHoldsSafe to rest an affirmation on
asserted_cuisConcepts the passage affirms about the patientYes
mentioned_cuisEvery concept the passage is about, including negated onesNo
family_history_cuisFindings about relativesNo

Material whose licence forbids redistribution raises an error in both stores rather than relying on a filter someone might forget. Material whose terms nobody has checked is treated the same way until someone checks.