What does recording-to-obsidian do?
Turn one meeting or one lecture into a self-contained, fully linked part of an Obsidian vault. Both modes share the pipeline, the conventions and the verification step; they differ in what they extract and how the notes are shaped.
| Meeting mode | Lecture mode | |
|---|---|---|
| Purpose | remember what was decided and who does what by when | re-learn the ideas quickly at any time |
| Hub | one meeting hub per session + a series MOC with one entry per meeting | one MOC per lecture |
| Body notes | ADR-style decision records; topic notes for durable knowledge | anchor notes (dense) + atomic notes (one idea each) |
| Traceability | transcript timestamps [00:12:34] on decisions and action items | PDF++ slide links [[deck.pdf#page=5]]; timestamps for spoken-only content |
| Reference | references/meeting-mode.md | references/lecture-mode.md |
<skill> below means the folder this file is in.
1. Pipeline
materials ──► 1 setup ──► 2 transcribe ──► 3 pick mode ──► 4 note-writer ──► 5 verify ──► 6 report
config, scripts/ meeting or writes notes check_vault
python transcribe.py lecture (one subagent) scan_secrets
1.1 Setup
Note the start time first: run date +%s and keep the number; verification checks only notes written after it.
Read <skill>/config.toml. It sets the transcription backend/model/language, diarization, notes.output_language, notes.default_mode and the vault folder names (notes_dir, moc_dir, meetings_dir, sources_dir; defaults used below). The interpreter for the scripts is runtime.python; if that is empty use ~/.local/share/recording-to-obsidian/venv/bin/python. Call it $PY — that is notation: in commands write the interpreter's and the scripts' full literal paths, one command per call, without shell variables or cd … && chains (agent permission rules match the literal command, and variables make it uncheckable). If it does not exist, tell the user to run install.sh (README) — do not pip-install into the user's own Python environments.
Establish the facts before anything else (ask only for what you cannot infer):
- vault — the user's path, else the current working directory if it contains
.obsidian/or the user said "this vault"; write only inside it; - materials — absolute paths of every file; classify each by extension: audio/video, subtitles (
.srt/.vtt), slides (.pdf/.pptx), notebooks/code, images/photos, documents; - title, series/course, date — from the user, file names, or the materials; never invent a date or attendee list (write
TBDand say so); - output language —
notes.output_languageunless the user asked for one.
1.2 Transcribe
For every recording without a matching subtitle file (same stem), run:
$PY <skill>/scripts/transcribe.py <recording> -o <scratch>/transcripts
<scratch>is a temporary folder outside the vault (e.g.$TMPDIR/recording-to-obsidian-<date>-<slug>), used for all intermediate files.- Options when needed:
--model small(faster, rougher),--language en(skip detection),--diarize(speaker labels; needsinstall.sh --diarizeand a Hugging Face token in the env var named bydiarize.hf_token_env). A first run downloads the model, which can take minutes; it prints progress on stderr — let it finish. - It writes
<stem>.srtand<stem>.txt(one[HH:MM:SS] Speaker N: textline per segment) and prints a JSON summary line (language, model, duration, speakers, cleaning stats, file paths). Keep that summary: language, model and diarization go into the notes' Source section. - Existing subtitles from any tool can be normalised the same way:
$PY <skill>/scripts/transcribe.py talk.vtt -o <scratch>/transcripts(no model involved; repetition loops are folded and a.txtwith timestamps is written). - Long recordings are fine: the script handles hours of audio; do not split the file.
If transcription fails, report the error line; $PY <skill>/scripts/transcribe.py --check shows what is missing.
1.3 Pick the mode
An explicit mode from the user (or notes.default_mode other than auto) wins. Otherwise:
- Slides, notebooks, lab code or handouts among the materials → lecture.
- Else read the first ~5 minutes of the transcript and skim the rest:
| Meeting signals | Lecture signals |
|---|---|
| several speakers with comparable airtime | one dominant speaker explaining |
| agenda, status updates, "where are we on…" | "today we'll cover…", "last week we saw…" |
| proposals, trade-offs argued, "let's go with…", "agreed" | definitions, derivations, worked examples, exercises |
| owners and dates: "can you take that by Friday?" | audience questions asked to understand, not to decide |
| projects, customers, deadlines, risks | course / lecture numbering, readings, homework |
- Mixed cases go by purpose: design reviews, planning, retros, debriefs and 1:1s are meetings; recorded tech talks, trainings, seminars and tutorials are lectures, even with some discussion.
Record the choice and the two or three signals that drove it (they go in the report). If it is a genuine toss-up and you can ask, ask; otherwise pick and say why.
1.4 Execution model: orchestrator + one note-writer
Reading a whole transcript, a deck page by page and notebooks, then writing tens of files fills a context window fast, so split the roles:
- Orchestrator — the session that received the request. It does 1.1–1.3 (transcription output stays on disk; it reads only enough of the transcript to pick the mode), hands the writing to one note-writer subagent, then runs the verification in 1.5 and reports.
- Note-writer — one subagent that does the whole writing job for the chosen mode in its own context: read every material, plan, write, self-check. One meeting or one lecture fits comfortably in one context; do not split pages or transcript chunks across agents (that loses cross-references and duplicates notes).
Do the work yourself instead of delegating when no subagent tool is available, when a subagent could not do what the job needs (see images, run scripts, write files). In meeting mode you may also write directly when the meeting is short enough that the brief would be as long as the work (a recording under ~15 minutes with no slides or documents). Lecture mode always uses the note-writer unless a subagent is unavailable or cannot do the job: a deck has to be read page by page, and that belongs in the writer's context, not yours. Say which you chose in the report. Verification (1.5) is never skipped. For several sessions, use one note-writer per session, sequentially when they append to the same MOC.
Brief for the note-writer — it starts with no context, so give it everything:
- this skill's absolute path; the mode; the files to follow:
references/meeting-mode.mdorreferences/lecture-mode.md, plusreferences/note-templates.md,references/transcript-processing.md,references/mermaid-style.md; - vault path, scratch folder,
$PY, output language, title/series/date (or "derive"), folder names from config; - absolute paths of every material and of the transcript files, with the transcription summary (language, model, speakers);
- the existing series/course MOC and the previous session's hub/MOC if any; anything the user asked for specifically;
- rules: write only inside the vault; never modify pre-existing notes except the documented appends; no secrets;
- what to return: the report block from Section 3 plus anything it could not resolve.
1.5 Verify (orchestrator)
When the note-writer returns, check its work independently rather than trusting its summary:
$PY <skill>/scripts/check_vault.py <vault> --since <epoch seconds when the job started>
$PY <skill>/scripts/scan_secrets.py <vault>/sources
check_vault.py must end with issues: 0; fix warnings too unless there is a reason not to (say which). It resolves links the way Obsidian does (file name or partial path, case-insensitive, ignoring #heading/|alias, skipping code blocks), so a broken link it reports is broken in Obsidian. It checks: broken [[links]]/embeds/markdown links, PDF++ links without .pdf, frontmatter (title, type, date_created), hub coverage (every new note is linked from a MOC or meeting hub; every meeting hub from a MOC), secrets; warnings for notes without connections, unstyled Mermaid and ambiguous links.
Then check what the scripts cannot:
- no pre-existing note changed except the documented appends (series MOC entry, superseded decision status, previous lecture MOC's "next" link);
- meeting mode: every decision has a record or is explicitly minor; every action item has an owner (or
TBD), a due date (orTBD) and a timestamp; - lecture mode: the MOC lists every new note exactly once; the granularity and anchor targets of the lecture self-check are met.
Fix small problems directly; send larger ones back to the note-writer with the exact list. Re-run check_vault.py until it reports 0 issues. Delete the scratch folder (it contains the wav and page images) unless the user wants it.
2. Shared conventions
Vault layout — notes are flat; navigation comes from MOCs and hubs, not folders.
vault/
├── notes/ # atomic, anchor, decision and topic notes (flat)
├── meetings/<series>/ # meeting hubs: "YYYY-MM-DD <title>.md"
├── moc/<series or course>/ # series MOC (meetings) / one MOC per lecture
└── sources/<series or course>/<YYYY-MM-DD or Lecture_NN>/
├── <prefix>_transcript.srt / .txt
├── <prefix>.pdf # slides, for PDF++ links
├── slide_images/ whiteboard_photos/ notebooks/
└── structured_slides.json # optional per-page reading record
Give every file copied into sources/ a series/course + session prefix (Atlas_2026-11-12_transcript.srt, Optics101_L03.pdf, Optics101_L03_p08.jpg): Obsidian resolves links by file name, so two sessions that both contain transcript.srt or page-08.jpg would cross-link. Folder names use the series/course name with spaces replaced by -.
File names — the human-readable title, no IDs (meeting hubs are the exception: they start with the date so they sort). Avoid / \ : * ? " < > | # ^ [ ]; write — instead of :. Titles are specific enough to stand alone: Rate limits use a token bucket per API key.md, not Rate limiting.md.
Frontmatter — every note starts with YAML frontmatter with at least title, type and date_created. type is moc, meeting, decision or topic (meeting mode) and moc, anchor or atomic (lecture mode). Mode-specific fields are in references/note-templates.md. Tags are lowercase English kebab-case: one series/course tag, 2–4 topic tags, one type tag; reuse tags already in the vault before inventing new ones.
Language — output_language = "auto" means: write in the language of the materials (the transcript's detected language, or the slides'). When the output language differs from the materials' language, keep technical terms in the original language and gloss each on first occurrence — Chinese notes from an English meeting: 功能开关 (feature flag); English notes from a German lecture: error propagation (Fehlerfortpflanzung) — then use whichever form reads naturally. Code, commands, paths, product and tool names are never translated. Headings follow the output language.
Links — every [[wikilink]] must resolve to a note or file in the vault. Link to an existing note instead of writing a duplicate. Use a path ([[sources/Atlas/2026-11-12/Atlas_2026-11-12_transcript.srt|transcript]]) when a bare name could be ambiguous. PDF++ page links always keep .pdf: [[Optics101_L03.pdf#page=5|Slide p.5 "Snell's law"]].
Timestamps — cite the transcript's [HH:MM:SS] for every decision, action item and claim that comes only from speech. They are the audit trail back to the recording.
Secrets — never write credentials into the vault. Before copying notebooks or code into sources/, run $PY <skill>/scripts/scan_secrets.py --redact <copy> on the copy (values become <REDACTED>, variable names stay). Spoken passwords, card numbers or sensitive personal data in a meeting are not minuted either — write "credentials were shared verbally (not recorded here)".
Visuals — read references/mermaid-style.md before writing any Mermaid diagram: it has the palette and the rules that keep diagrams rendering. Invalid syntax is worse than no diagram.
Mode outputs
- Meeting →
references/meeting-mode.md: 1 hub, 1 decision record per significant decision, 0–N topic notes for knowledge worth keeping, one entry appended to the series MOC (created on first use), transcript copied tosources/. - Lecture →
references/lecture-mode.md: 1 MOC, 8–15 anchor notes for a normal 30+ page deck, on average at least one atomic note per content slide, slides/notebooks/transcript copied tosources/.
3. Report
Meeting mode:
Meeting 2026-11-12 — <title> (series: <series>) mode: meeting (signals: …)
Transcript: 00:41:07, mlx:whisper-large-v3-turbo, 3 speakers, en → notes en
Hub: meetings/<series>/2026-11-12 <title>.md
Decisions: 3 (records: 3) Action items: 7 (owner TBD: 1, due TBD: 2)
Topic notes: 2 Open questions: 2 Risks: 1
Series MOC: moc/<series>/MOC — <series>.md (entry appended)
check_vault: issues 0, warnings 0
Unresolved: <speakers not matched to names, unclear passages, …>
Lecture mode:
Lecture NN — <topic> mode: lecture (signals: …)
Slides: 45 pages (content 37) Knowledge points: 68
Notes: 56 atomic + 12 anchor archetypes: concept=34, practice-demo=6, …
Notes per content slide: 1.5 Anchors with ≥2 rich elements: 12/12
Mermaid: 14 (types: sequence, mindmap, flowchart, …) Slide images: 11
check_vault: issues 0 MOC coverage: 68/68
MOC: moc/<course>/MOC — … .md Sources: sources/<course>/Lecture_NN/
Unresolved: …