Convert Recording → Knowledge Markdown
Emit outbox/{meeting-slug}/transcript.md; screenshots inline when useful; structured docs only on request. {meeting-slug} = kebab-case-topic-YYYYMMDD. Never touch inbox/ or knowledge/.
References (load on demand): setup · output templates · ffmpeg · transcribe CLI · prep audio CLI · chunked transcription · structured docs · cleanup CLI · spot-check: scripts/extract_clip.py.
Scripts run via uv run --script (PEP 723). All support --help. On first run verify setup prerequisites.
Workflow
Step 0 — Intake (mandatory, before any API call)
Ask verbatim:
"Before I process this, three quick things:
- Language of the conversation? (e.g. en, ru, fr — used as
--languagehint)- How many speakers?
- Topic / domain in one line, plus any proper names or specialized terms."
Record answers; sensible defaults if user skips a field. Never silently transcribe without asking.
Technical / multilingual / mumbled audio → VTT-aligned path strongly preferred (if VTT available). Without VTT, diarize fallback with 8-min chunks; warn user about quality.
Steps 1–6
Inventory + probe —
lsinbox,ffprobemedia. VTT present → parse via parse_vtt.py →tmp/prep/<slug>/vtt_cues.json; sample cues, assess quality (speaker count, garble, gaps, proper-noun fidelity). Non-English VTTs: screen-reference detection won't fire — read cues directly.Gate 1 — present findings + plan +
{meeting-slug}. Determine transcription path:Scenario Path VTT good quality Render VTT directly; skip prep + API VTT exists, text garbled VTT-aligned retranscription: VTT as skeleton (speakers + timestamps) + gpt-4o-transcribefor text quality → agent alignsNo VTT, single speaker gpt-4o-transcribedirectlyNo VTT, multi-speaker Diarize fallback: gpt-4o-transcribe-diarizeat 8-min chunks (known unstable — warn user)Preprocess + transcribe — run prep_audio.py on input (audio or video; extracts audio in-pass; source video retained for screenshots).
VTT-aligned path (primary for retranscription):
- VTT provides speaker labels + turn timestamps; transcription provides clean text
- Run
gpt-4o-transcribeonstripped.ogg(or per-chunk if >8 min) with--prompt(vocab + 1-line topic only) - Agent aligns clean text to VTT turns — this is language work you perform directly. Match transcribed text to VTT turn boundaries using VTT text as positional guide. Preserve VTT speaker labels and timestamps. See VTT-aligned merge.
Single-speaker path:
- Run
gpt-4o-transcribeonstripped.ogg(or per-chunk if >8 min) with--prompt - Output is the transcript directly; no alignment needed
Diarize fallback (no VTT, multi-speaker):
- Chunks at 8 min max (diarize model unstable on longer audio)
- Per-chunk:
transcribe_diarize.py --manifest --chunk-index N; thenmerge_chunks.py - Warn user: diarization quality is unreliable; may need manual correction
- See chunked transcription
All paths:
--languagerequired. On non-zero exit → surface stderrError [<category>]:, ask wait/cancel. See exit codes.Speaker labelling —
- VTT-aligned: speakers come from VTT; confirm with user (VTT labels may be generic like "Speaker 1").
- Diarize fallback:
render_transcript.py --samples <json>→ user names speakers. - Single-speaker: user provides name or default.
- Then render via
render_transcript.py --speakers ... --out outbox/{slug}/transcript.md. - For diarize long path: cleanup pass as language work on
merged.json→polished.json. See Cleanup Pass.
Transcript (mandatory artifact) — produced by step 4. For exact markdown shape see output templates § transcript.md. Faithful to meaning; repair recoverable garble; never invent. See Fidelity rule.
Screenshots — skip if no screen content (faces only → zero screenshots). Take frame at
timestamp + 2s,-q:v 2. Source video frommanifest.jsonsourcefield. For format see output templates § screenshots.- UI / slides / docs → screenshots in scope
- Diagram on screen → screenshot and Mermaid
- Data table → screenshot and markdown table
Steps 7–10 — Structured docs (conditional)
Gate 2 — ask: structured docs or transcript only? If transcript only → cleanup + report + stop.
Otherwise → structured docs reference: plan topics, emit summary.md + topics/{slug}.md, cleanup, report, stop.
Step 11 — Cleanup (mandatory, after user accepts)
Once the user confirms the results are acceptable, remove temporary prep artifacts via cleanup.py:
uv run --script scripts/cleanup.py --slug {meeting-slug}
The script verifies outbox/{meeting-slug}/transcript.md exists before deleting tmp/prep/{meeting-slug}/. Use --dry-run to preview. Never use rm directly — all temp removal goes through this script.
Fidelity rule
Transcript captures what was said and meant, not the literal sound stream. In priority order:
- Preserve every substantive turn — decisions, claims, questions, objections, reactions. If spoken, it appears.
- Repair recoverable garble — when ASR returns gibberish and intent is recoverable from parallel clean pass or context, replace with faithful version. Selecting better evidence ≠ paraphrasing.
- Drop fabrications — sentences introducing concepts absent from any source signal (prompted model hallucinations during silence).
- Never invent — don't fill silences with plausible speech.
When two transcripts agree on meaning → faithful. When they disagree and neither is recoverable → mark unclear.
Anti-patterns
- Don't fabricate. Inventing dialog is the worst failure mode — worse than garble. See fidelity rule.
- Don't write language-processing scripts. No
difflib, regex tables, word-overlap heuristics, jq/sed/awk for cleanup. The cleanup pass is language work you perform directly. Scripts = I/O plumbing only. - Don't load rich
--prompton non-diarize model. Prompts leak as fabrications. Vocab list + 1-line topic max. See prompt-hallucination warning. - Don't auto-pick VTT decisions. Always surface assessment; user confirms at Gate 1.
- Don't write outside
outbox/{meeting-slug}/. Temp files →tmp/; finals →outbox/. - Don't run
rmon temp files. Always usecleanup.py— it validates output exists and only operates undertmp/.