medical-records
Skill3 starsChanged 3 months agoMITView on GitHub
File a medical After Visit Summary into the Obsidian vault as a structured, queryable record. Extracts vitals and visit metadata from the document text, writes a dated note with flat Number frontmatter properties (so Obsidian Bases and Dataview can trend vitals across visits), archives and embeds the source scan, and slots the note into the Medical Vitals base. Usually invoked by the scan-document router when it classifies a scan as an after-visit summary, but can be run directly when the user says 'file this doctor visit', 'capture this after-visit summary', or 'log my visit vitals'. v1 handles after-visit summaries only.
In this repository41 other files it gives its agents.
- cadence-goalsSkill
- ci-feedback-loopSkill
- compoundSkill
- decision-rights-doctrineSkill
- diataxisSkill
- dispatch-gateSkill
- drift-checkSkill
- family-checkSkill
- fix-then-re-review-ladderSkill
- github-projectsSkill
- gitSkill
- goal-treeSkill
- handoffSkill
- improvement-loopSkill
- kb-captureSkill
- kb-compileSkill
- kb-coreSkill
- kb-lessons-bridgeSkill
- kb-lintSkill
- l1-supervisorSkill
- l2-supervisorSkill
- lessons-learnedSkill
- lN-lifecycle-doctrineSkill
- lN-review-doctrineSkill
- operator-interview-doctrineSkill
- operator-interviewSkill
- operator-reviewSkill
- planning-workflowSkill
- pr-review-fanoutSkill
- pull-taskSkill
- ralph-wiggumSkill
- reviewSkill
- root-cause-depthSkill
- scan-documentSkill
- self-improvementSkill
- self-verifySkill
- skillifySkill
- task-workflowSkill
- testing-without-mocksSkill
- vault-cloudSkill
- verify-inherited-premiseSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 114
Ambiguous visit date: if the appointment date isn't clearly recoverable, ask (AskUserQuestion) rather than guessing.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
medical-records — file an After Visit Summary
A handler skill: turns the text of a medical After Visit Summary into a structured vault record. Consumes OCR'd text (from scan-document, or produced here if invoked directly) and the source file; produces a dated note + archived scan.
When this fires
- Invoked by
scan-documentfor classmedical.after-visit-summary. - Direct: "file this doctor visit", "capture this after-visit summary", "log my visit vitals".
Trigger signals (how scan-document recognizes this)
This handler owns its own recognition vocabulary — scan-document's router does not hardcode these; it reads this section to decide whether a scan belongs here. A document is an After Visit Summary when the recognized text shows several of:
- Header/title text: "After Visit Summary"
- An MRN (medical record number)
- Vitals (blood pressure, weight, pulse, temperature, BMI, oxygen saturation, etc.)
- A provider/clinic/institution name
- A follow-up date or discharge instructions
One signal alone may be coincidental (e.g. a phone number that looks like an MRN); several together are a confident match. Ambiguous cases should be confirmed with the user rather than guessed.
Inputs
- Path to the source scan (PDF/image).
- The recognized text. If not supplied (direct invocation), OCR it first:
pdftotext -layout "<file>" -fast path (only if thepdftotextbinary is present — it is not part of stock macOS; treat "command not found" as "skip to Vision," not an error), elseswift <scan-document-dir>/scripts/ocr.swift "<file>".
Command safety: the file path is untrusted input (user- or filesystem-derived). Always pass it as a single quoted argument to any shell command — never interpolate it unquoted — and reject/validate it if it contains shell metacharacters (; | & $ \ ( ) < > newline`) before invocation.
Steps
- Extract these fields from the text (leave a field out if genuinely absent — do not invent):
- Visit metadata:
visit_date(the appointment date), provider/institution, department, department phone, MRN, reason(s) for visit, referral(s) + expiry, follow-up date, medications. - Vitals: blood pressure (systolic + diastolic separately), weight, BMI, height, pulse, temperature, oxygen saturation.
- Visit metadata:
- Resolve the vault via the
obsidian-noteshost-config (~/.claude/hosts/<hostname>.md). Bail clearly if none is configured. - Path safety:
slugandvisit_dateare both derived from OCR'd text and are therefore untrusted — sanitize both before using them to build anyAttachments/orNotes/path. Constrainslugto[a-z0-9-](lowercase, strip/replace anything else, collapse repeats). Validatevisit_dateas strict ISOYYYY-MM-DD; if it doesn't match, treat it as unrecoverable and fall through to the "ambiguous visit date" edge case rather than using the raw value in a path. Never write an unvalidatedslugorvisit_dateinto a filesystem path — this blocks path traversal (../, absolute paths, embedded separators) riding in through OCR text. - Archive the scan: copy the source file into
<vault>/Attachments/named<visit_date>-<slug>.<ext>(e.g.2026-06-03-osu-sleep-avs.pdf), using the sanitized values from step 3. The attachment is a source artifact — name it by the document's own date for findability. Verify the filename is unique in the vault (the embed resolves by filename). - YAML safety:
title,provider,department,department_phone, eachvisit_reasonitem,referral, andmedicationsare all derived from OCR'd text and are therefore untrusted — never write any of them into frontmatter unquoted. Emit every OCR-derived scalar as a double-quoted YAML string with internal"escaped as\"and\escaped as\\(or as a YAML block scalar for long/multi-line values). A stray:, a leading-, an embedded newline, or a line that merely resembles---orkey: valuein the source text can otherwise break the YAML frontmatter block or inject extra properties — and medical text is full of colons (times, ratios, dosages). This applies to every scalar in the schema below sourced from OCR text, not just the ones named here. - Write the note at
<vault>/Notes/<write-year>/<write-month>/<write-date>-<slug>.md(sanitizedslug):- Filename prefix = today's write date, NOT the visit date. The prefix is a de-dupe / folder-partition key; the visit date lives in frontmatter as
visit_date. - Frontmatter — the schema (see below). Vitals are flat, top-level, Number-typed properties. This is deliberate and load-bearing (see "Why this schema").
- Body: visit header, a human-readable vitals table, plan & follow-up, a condensed instructions summary (keep patient-specific guidance; the full text lives in the embedded PDF), and the embedded source (
![[<attachment>]]).
- Filename prefix = today's write date, NOT the visit date. The prefix is a de-dupe / folder-partition key; the visit date lives in frontmatter as
- Report where the note and attachment landed, and note that it will appear in the Medical Vitals base.
Frontmatter schema
Every OCR-derived scalar below (title, provider, department, department_phone, visit_reason items, referral, medications) MUST be written as a quoted, YAML-safe string — see "YAML safety" in Steps above. Do not write raw OCR text into frontmatter.
---
title: <provider> — After Visit Summary
type: medical-visit
visit_date: 2026-06-03 # semantic date (Date type)
tags:
- medical/visit
- <specialty> # e.g. sleep-medicine
provider: <institution>
department: <department>
department_phone: <phone>
mrn: "<mrn>" # quoted — keep as Text, not a number
visit_reason:
- <reason>
referral: <referral + expiry> # omit if none
follow_up: 2026-12-03 # omit if none
medications: <text or "none prescribed">
source_pdf: "[[<attachment filename>]]"
captured: 2026-07-13 # write date; matches filename prefix
# --- vitals: flat Number properties → Bases columns / Dataview trending ---
bp_systolic: 122
bp_diastolic: 68
weight_lb: 188
bmi: 26.98
height_in: 70
pulse_bpm: 77
temp_f: 97.3
spo2_pct: 99
---Why this schema (do not "improve" it back to the obvious-but-wrong forms)
Verified against Obsidian's official Properties and Bases docs:
- Vitals are flat top-level properties, not a nested
vitals:object. Obsidian's Bases model is literally "each row is a file, each column is a property of that file," and Bases/Dataview read frontmatter only, never the note body. Nested properties are not supported in the Properties UI. So flat frontmatter is the correct — and only — form that serves both Bases and Dataview. - Blood pressure is split into two Numbers (
bp_systolic/bp_diastolic), not a single122/68string. Obsidian has 6 property types (Text, List, Number, Checkbox, Date, Date&time) and no unit type; a122/68value is Text and can't be trended. Split → both Numbers → aggregatable. - Numbers are bare (no quotes, no units in the value) so they type as Number. Units go in the key (
weight_lb,temp_f) since there is no unit type.mrnis the exception — quote it so it stays Text. - No
vital_prefix.type: medical-visitalready scopes these; clean names make better Base columns.
The Medical Vitals base
Records feed <vault>/Areas/Health/Medical Vitals.base — a table filtered to type == "medical-visit", sorted by visit_date, with the vitals as columns and a bp formula (bp_systolic + "/" + bp_diastolic) for display. It exists after the first record; adding visits just adds rows. If it is missing, recreate it with that shape.
Edge cases
- Missing vitals: omit absent properties rather than writing nulls or zeros — a zero would corrupt trend averages.
- Ambiguous visit date: if the appointment date isn't clearly recoverable, ask (AskUserQuestion) rather than guessing; it is the semantic key.
- Non-AVS medical doc (lab result, imaging, immunization): out of scope for v1 — say so and fall back to the router's generic capture. Those get their own handlers later.
- Height unchanged across visits: fine to carry it each time; it makes each record self-contained.
- Sensitive content: these records carry MRN and vitals — private medical data by nature. Capture into the local vault only; never send content (including the file, extracted text, or any field) to an external service without explicit operator say-so.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

