Vibecode tl;dv
track this build4 phases, 12 steps, beginner friendly0%A DIY app can record, transcribe, summarize, and generate clips, but tl;dv's team library, CRM integrations, and meeting-bot reliability are real work.
You are building a lean indie version of tl;dv. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # tl;dv · indie build A local recorder whose hard problems are capturing hours of audio without losing any of it, presenting a transcript that invents text as something the reader is expected to verify, and keeping a growing pile of recordings of other people under control. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Record to short numbered segment files on disk, never one growing buffer | a three hour meeting that dies at minute one hundred and seventy should cost you one segment | | Transcription | A local speech model by default, a hosted one only if a key is present | audio of other people talking is the last thing that should leave the machine by default | | Summary | One model call over the transcript, every bullet carrying the line ids behind it | the published model card warns the transcript can contain text nobody said | | Library | SQLite index over a directory of meeting folders you can open in a file manager | the recordings should outlive this application | | Retention | Audio deleted on a schedule, transcript and notes kept | a recording of a colleague is a liability with a shelf life | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the recorder, the library and the workers. - Get it: nodejs.org, or your version manager. - Verify: node --version prints v22 or higher - [ ] **ffmpeg** · free - Why: Segment recording, clip cutting and format conversion all go through it, and the clip boundary work depends on its behaviour. - Get it: Your package manager, or the official builds. - Verify: ffmpeg -version prints a version and the build lists your audio input device - [ ] **A local speech recognition model and runtime** · free, plus disk for the model - Why: The default path keeps audio of other people on your machine, and the model size you choose decides both accuracy and how long a meeting takes to process. - Get it: Download a runtime and one model file, and note which model you used. - Verify: A two minute recording transcribes with timestamps in a time you are willing to wait - [ ] **A working system audio capture route on your platform** · free, or the price of a virtual device - Why: Recording the other side of a call is the part that differs most between operating systems and versions, and it decides whether this tool is useful or just records you. - Get it: Check the current documentation for your operating system version, then either use its supported capture interface or install a virtual audio device. - Verify: A test recording contains both your voice and audio played by another application - [ ] **What you will say before recording, and to whom** · free - Why: The rule here is that you announce it, and deciding the wording once means you actually do it rather than deciding in the moment. - Get it: Write the sentence you will say and the retention period you will offer, and put both in the README. - Verify: The consent prompt in the application matches what the README promises ## Quick start ```sh npm run export -- --meeting=2026-09-15-standup ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. - meeting bot upkeep - team/shared library - CRM integrations - admin controls - polished clip sharing If one of those is essential to you, that is the reason to keep paying for tl;dv, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · tl;dv The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local meeting recorder that transcribes what you were in the room for and keeps every file on your own machine to replace tl;dv. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Record only conversations you are part of, and say out loud that you are recording before you start. This build has no bot that joins a call on your behalf, no hidden capture, and no indicator you can switch off. The published model card for the open transcription models most people use for this says in as many words that they should not be used to transcribe recordings of individuals taken without their consent, and that is the rule here regardless of what the law where you live allows. Store the consent note with the meeting: who was told, and how. The second rule follows from the same model card, which states that predictions may include text that was not spoken in the audio at all. Speech recognition invents fluent sentences over silence, music and crosstalk, and a summary built on those sentences reads beautifully and is false. So no summary bullet exists in this application without the transcript lines it came from and a timestamp you can click to hear the audio. If a bullet cannot cite, it does not get displayed. Do not build a feature that mails a summary to other people, because you will not be there to check it. ### Stack (fixed, do not substitute) - Capture: Record to short numbered segment files on disk, never one growing buffer · a three hour meeting that dies at minute one hundred and seventy should cost you one segment - Transcription: A local speech model by default, a hosted one only if a key is present · audio of other people talking is the last thing that should leave the machine by default - Summary: One model call over the transcript, every bullet carrying the line ids behind it · the published model card warns the transcript can contain text nobody said - Library: SQLite index over a directory of meeting folders you can open in a file manager · the recordings should outlive this application - Retention: Audio deleted on a schedule, transcript and notes kept · a recording of a colleague is a liability with a shelf life ### Data model (create this before Phase 1) - meetings(id, title, started_at, ended_at, folder, consent_note, state) · consent_note records who was told and how - segments(id, meeting_id, ordinal, path, started_at, seconds, bytes) · the recording is a sequence of short files, not one buffer - lines(id, meeting_id, start_ms, end_ms, text, no_speech_prob, avg_logprob, model_id, chunk_ordinal) - markers(id, meeting_id, at_ms, label, created_at) · dropped live with a hotkey while the meeting runs - clips(id, meeting_id, marker_id, start_ms, end_ms, path, created_at) - summaries(id, meeting_id, model_id, prompt_version, created_at, bullets_json) · each bullet holds its text and the line ids it cites - retention(meeting_id, audio_delete_after, audio_deleted_at) · the transcript survives the audio - There is no column anywhere holding a summary sentence without a list of line ids beside it. A bullet that cannot cite is a bug. ### Phase 1 · Capture that survives a three hour call Build: Start and stop from a single control or command. Write fixed length segments, numbered, into a meeting folder, flushing each one as it closes. Show elapsed time, input level and a plainly visible recording state. Prompt for the consent note before the first segment is written, and store it with the meeting. Trap: Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. Done when: Killing the process mid-recording leaves every closed segment playable and loses at most the current one, tested by killing it twice at different points. Capturing system audio as well as the microphone is either working on your platform or the application says clearly that it is not and records the microphone only. The consent note is required before recording starts and is stored with the meeting, tested. Disk space is checked before starting and recording refuses to begin below a stated threshold. Do not build yet: No transcription, summary, clips or library yet. ### Phase 2 · A transcript the reader can check Build: Transcribe in overlapping chunks so no word falls on a boundary, keeping the model's own timestamps. Store per line the probability that the segment held no speech and the average confidence. Detect long silences before transcribing and skip them. Render the transcript with each line clickable to play the audio from that moment. Trap: Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. Done when: Every stored line has start and end milliseconds, a no-speech probability and an average confidence, checked in the database. Clicking any line plays the audio at that timestamp, within a stated tolerance, tested on a recording spanning several segments. A recording of pure silence produces no transcript lines at all, tested with a generated file. Chunk boundaries overlap and a word spoken across a boundary appears once, tested with a fixture. Do not build yet: No summary, markers, clips or retention yet. ### Phase 3 · A summary that cites or says nothing Build: One model call over the transcript asking for bullets and action items, each with the line ids it used. Drop any bullet whose cited lines do not exist or whose text does not appear near them. Render each bullet with its timestamps. Show the whole summary as unverified until a person has opened at least the action items. Trap: A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. Done when: A bullet whose cited line ids do not exist is dropped before display, tested with a stub returning invented ids. Every displayed bullet links to a timestamp that plays the audio. A transcript with no action items produces an empty action list rather than invented ones, tested with a fixture of small talk. The model identifier and prompt version are stored with every summary. Do not build yet: No markers, clips, search or retention yet. ### Phase 4 · Markers while it happens, clips afterwards Build: A global hotkey writes a marker with the elapsed milliseconds and an optional label, working while another application has focus. After the meeting, each marker becomes a clip with configurable lead-in and lead-out, cut across segment boundaries. Clips are files in the meeting folder with a row in the database. Trap: Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. Done when: A marker dropped while another application has focus lands within a stated tolerance of the moment the key was pressed, tested by hand against a clock. A clip spanning two segments plays continuously with no gap or repeat, tested. A clip requested across a missing segment fails with a message naming the gap instead of producing silence. Clips are ordinary media files in the meeting folder and play in any player. Do not build yet: No library search, retention or export yet. ### Phase 5 · A library that forgets on purpose Build: A local library page listing meetings with search across transcripts and notes. Delete audio after a configured retention while keeping transcripts, notes and clips, with the deletion recorded. A per-meeting delete removes the folder, the rows, the clips and the search index together. Export a meeting as a folder of plain files that opens without this application. Trap: Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. Done when: Audio older than the retention period is deleted on schedule and the transcript, notes and clips survive with a recorded deletion time, tested by moving the clock. Deleting a meeting removes the folder, every row and the search index entry, verified by scanning the directory and querying the index afterwards. Search returns a meeting by a phrase spoken in it and links to the timestamp. An exported meeting folder contains audio, transcript, notes and clips as ordinary files and is readable with no application installed. Do not build yet: Nothing after this. ### Out of scope (and why) - A bot that joins a video call on your behalf, which records people who were told a person was attending and is the part of the paid product most likely to breach a platform's terms. - Speaker identification, which needs enrolment audio per person and a stored voice profile of your colleagues, a much heavier thing to hold than a transcript. - Customer record synchronisation, which exists so a sales team can file a conversation, and carries the same consent question into somebody else's database. - A shared team library, because the moment recordings leave your machine you are operating a retention policy for other people's voices. ### README must contain - That recording other people requires telling them, that the consent note is stored with each meeting, and that this build has no hidden capture and no bot - That the published model card for open speech models warns predictions can include text that was never spoken, which is why every summary bullet carries citations - That the recording is a folder of numbered segments, so a crash costs seconds rather than the meeting - The retention period for audio, what survives when it expires, and exactly what deleting a meeting removes - Which platform permissions are needed for microphone and system audio capture, and what the application does when it cannot get them ===== AGENTS.md ===== # Agent instructions · tl;dv indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Record to short numbered segment files on disk, never one growing buffer, A local speech model by default, a hosted one only if a key is present, One model call over the transcript, every bullet carrying the line ids behind it, SQLite index over a directory of meeting folders you can open in a file manager, Audio deleted on a schedule, transcript and notes kept. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ===== BUILD_PLAN.md ===== # Build plan · tl;dv A local recorder whose hard problems are capturing hours of audio without losing any of it, presenting a transcript that invents text as something the reader is expected to verify, and keeping a growing pile of recordings of other people under control. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Capture that survives a three hour call Recording writes continuously to disk in short segments and a mid-meeting failure costs seconds, not hours. ### Steps 1. Write numbered segments and close each one The meeting is the folder; the file is only the last few seconds at risk. Files: `src/capture/recorder.ts`, `tests/segment-crash.test.ts` 2. Find out what your platform will actually give you Capturing another application's audio is an operating system question with a different answer on each one, so check the current documentation for your version before designing around it. Files: `docs/audio-capture.md` 3. Ask for the consent note before the first byte Afterwards it is a form nobody fills in. Files: `src/capture/start.ts` ### Done when - [ ] A kill mid-recording loses at most one segment - [ ] System audio either works or is honestly reported missing - [ ] The consent note is required and stored - [ ] Recording refuses to start on a nearly full disk ### Watch out - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. ## Phase 2 · A transcript the reader can check Every transcript line carries its timestamps and its confidence signals, and the audio behind it plays on a click. ### Steps 1. Transcribe in overlapping chunks and keep timestamps A boundary in the middle of a sentence is where transcripts go wrong. Files: `src/transcribe/chunks.ts`, `tests/overlap.test.ts` 2. Cut the silence before the model sees it Silence is where invented sentences come from. Files: `src/transcribe/vad.ts` 3. Store the confidence signals and show them The reader deciding whether to trust a line needs the number. Files: `src/transcribe/store.ts` ### Done when - [ ] Every line has timestamps and confidence signals - [ ] Clicking a line plays the matching audio - [ ] Silence produces no transcript lines - [ ] Overlapping chunks do not duplicate words ### Watch out - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. ## Phase 3 · A summary that cites or says nothing Notes and action items are generated only with the transcript lines they came from attached. ### Steps 1. Ask for line ids with every bullet A claim without a source is not a note, it is a guess. Files: `src/summary/prompt.ts` 2. Verify the citations before display The model will cite lines that do not exist. Files: `src/summary/verify.ts`, `tests/citations.test.ts` 3. Store model and prompt version with the summary So a strange bullet can be traced rather than argued about. Files: `src/summary/store.ts` ### Done when - [ ] Bullets with bad citations are dropped - [ ] Every bullet links to playable audio - [ ] No action items are invented from small talk - [ ] Model and prompt version are stored ### Watch out - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. ## Phase 4 · Markers while it happens, clips afterwards A hotkey during the meeting marks a moment, and each marker becomes a clip you can play or hand over. ### Steps 1. Register a hotkey that works without focus The moment worth clipping happens while you are looking at the call. Files: `src/capture/hotkey.ts` 2. Cut clips from the segment timeline Segment start and duration are the only reliable clock here. Files: `src/clips/cut.ts`, `tests/clip-boundary.test.ts` 3. Fail loudly across a gap A silent clip is a bug report you will never receive. Files: `src/clips/cut.ts` ### Done when - [ ] Markers land within tolerance without focus - [ ] Clips across segment boundaries play continuously - [ ] A clip across a gap fails with a clear message - [ ] Clips are ordinary files that play anywhere ### Watch out - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ## Not in this build - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. ## After v1, if you want it - A per-meeting cost and time report, so you know what a three hour recording actually costs to process - Transcript correction that feeds back into the summary, since the corrections are the only ground truth you will have - A weekly digest of your own action items, generated locally and never sent to anybody else ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. One folder per meeting holding segments, transcript, notes and clips. MEETINGS_DIR=~/Meetings # Required. The index over the meeting folders. Rebuildable from them. DATABASE_PATH=./data/meetings.db # Required. Segment length. This is how much a crash can cost you. SEGMENT_SECONDS=60 # Required. The local speech model. Its identifier is stored with every transcript line. ASR_MODEL_PATH=./models/speech-medium.bin # Optional · secret. Optional hosted model for summaries. Without it the transcript and clips still work. SUMMARY_API_KEY=sk-...redacted # Required. Audio is deleted after this. Transcripts, notes and clips survive. AUDIO_RETENTION_DAYS=30 # Required. Lead-in and lead-out either side of a marker when cutting a clip. CLIP_LEAD_SECONDS=30
You are building a lean indie version of tl;dv. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # tl;dv · indie build A local recorder whose hard problems are capturing hours of audio without losing any of it, presenting a transcript that invents text as something the reader is expected to verify, and keeping a growing pile of recordings of other people under control. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Record to short numbered segment files on disk, never one growing buffer | a three hour meeting that dies at minute one hundred and seventy should cost you one segment | | Transcription | A local speech model by default, a hosted one only if a key is present | audio of other people talking is the last thing that should leave the machine by default | | Summary | One model call over the transcript, every bullet carrying the line ids behind it | the published model card warns the transcript can contain text nobody said | | Library | SQLite index over a directory of meeting folders you can open in a file manager | the recordings should outlive this application | | Retention | Audio deleted on a schedule, transcript and notes kept | a recording of a colleague is a liability with a shelf life | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the recorder, the library and the workers. - Get it: nodejs.org, or your version manager. - Verify: node --version prints v22 or higher - [ ] **ffmpeg** · free - Why: Segment recording, clip cutting and format conversion all go through it, and the clip boundary work depends on its behaviour. - Get it: Your package manager, or the official builds. - Verify: ffmpeg -version prints a version and the build lists your audio input device - [ ] **A local speech recognition model and runtime** · free, plus disk for the model - Why: The default path keeps audio of other people on your machine, and the model size you choose decides both accuracy and how long a meeting takes to process. - Get it: Download a runtime and one model file, and note which model you used. - Verify: A two minute recording transcribes with timestamps in a time you are willing to wait - [ ] **A working system audio capture route on your platform** · free, or the price of a virtual device - Why: Recording the other side of a call is the part that differs most between operating systems and versions, and it decides whether this tool is useful or just records you. - Get it: Check the current documentation for your operating system version, then either use its supported capture interface or install a virtual audio device. - Verify: A test recording contains both your voice and audio played by another application - [ ] **What you will say before recording, and to whom** · free - Why: The rule here is that you announce it, and deciding the wording once means you actually do it rather than deciding in the moment. - Get it: Write the sentence you will say and the retention period you will offer, and put both in the README. - Verify: The consent prompt in the application matches what the README promises ## Quick start ```sh npm run export -- --meeting=2026-09-15-standup ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. - meeting bot upkeep - team/shared library - CRM integrations - admin controls - polished clip sharing If one of those is essential to you, that is the reason to keep paying for tl;dv, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · tl;dv The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local meeting recorder that transcribes what you were in the room for and keeps every file on your own machine to replace tl;dv. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Record only conversations you are part of, and say out loud that you are recording before you start. This build has no bot that joins a call on your behalf, no hidden capture, and no indicator you can switch off. The published model card for the open transcription models most people use for this says in as many words that they should not be used to transcribe recordings of individuals taken without their consent, and that is the rule here regardless of what the law where you live allows. Store the consent note with the meeting: who was told, and how. The second rule follows from the same model card, which states that predictions may include text that was not spoken in the audio at all. Speech recognition invents fluent sentences over silence, music and crosstalk, and a summary built on those sentences reads beautifully and is false. So no summary bullet exists in this application without the transcript lines it came from and a timestamp you can click to hear the audio. If a bullet cannot cite, it does not get displayed. Do not build a feature that mails a summary to other people, because you will not be there to check it. ### Stack (fixed, do not substitute) - Capture: Record to short numbered segment files on disk, never one growing buffer · a three hour meeting that dies at minute one hundred and seventy should cost you one segment - Transcription: A local speech model by default, a hosted one only if a key is present · audio of other people talking is the last thing that should leave the machine by default - Summary: One model call over the transcript, every bullet carrying the line ids behind it · the published model card warns the transcript can contain text nobody said - Library: SQLite index over a directory of meeting folders you can open in a file manager · the recordings should outlive this application - Retention: Audio deleted on a schedule, transcript and notes kept · a recording of a colleague is a liability with a shelf life ### Data model (create this before Phase 1) - meetings(id, title, started_at, ended_at, folder, consent_note, state) · consent_note records who was told and how - segments(id, meeting_id, ordinal, path, started_at, seconds, bytes) · the recording is a sequence of short files, not one buffer - lines(id, meeting_id, start_ms, end_ms, text, no_speech_prob, avg_logprob, model_id, chunk_ordinal) - markers(id, meeting_id, at_ms, label, created_at) · dropped live with a hotkey while the meeting runs - clips(id, meeting_id, marker_id, start_ms, end_ms, path, created_at) - summaries(id, meeting_id, model_id, prompt_version, created_at, bullets_json) · each bullet holds its text and the line ids it cites - retention(meeting_id, audio_delete_after, audio_deleted_at) · the transcript survives the audio - There is no column anywhere holding a summary sentence without a list of line ids beside it. A bullet that cannot cite is a bug. ### Phase 1 · Capture that survives a three hour call Build: Start and stop from a single control or command. Write fixed length segments, numbered, into a meeting folder, flushing each one as it closes. Show elapsed time, input level and a plainly visible recording state. Prompt for the consent note before the first segment is written, and store it with the meeting. Trap: Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. Done when: Killing the process mid-recording leaves every closed segment playable and loses at most the current one, tested by killing it twice at different points. Capturing system audio as well as the microphone is either working on your platform or the application says clearly that it is not and records the microphone only. The consent note is required before recording starts and is stored with the meeting, tested. Disk space is checked before starting and recording refuses to begin below a stated threshold. Do not build yet: No transcription, summary, clips or library yet. ### Phase 2 · A transcript the reader can check Build: Transcribe in overlapping chunks so no word falls on a boundary, keeping the model's own timestamps. Store per line the probability that the segment held no speech and the average confidence. Detect long silences before transcribing and skip them. Render the transcript with each line clickable to play the audio from that moment. Trap: Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. Done when: Every stored line has start and end milliseconds, a no-speech probability and an average confidence, checked in the database. Clicking any line plays the audio at that timestamp, within a stated tolerance, tested on a recording spanning several segments. A recording of pure silence produces no transcript lines at all, tested with a generated file. Chunk boundaries overlap and a word spoken across a boundary appears once, tested with a fixture. Do not build yet: No summary, markers, clips or retention yet. ### Phase 3 · A summary that cites or says nothing Build: One model call over the transcript asking for bullets and action items, each with the line ids it used. Drop any bullet whose cited lines do not exist or whose text does not appear near them. Render each bullet with its timestamps. Show the whole summary as unverified until a person has opened at least the action items. Trap: A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. Done when: A bullet whose cited line ids do not exist is dropped before display, tested with a stub returning invented ids. Every displayed bullet links to a timestamp that plays the audio. A transcript with no action items produces an empty action list rather than invented ones, tested with a fixture of small talk. The model identifier and prompt version are stored with every summary. Do not build yet: No markers, clips, search or retention yet. ### Phase 4 · Markers while it happens, clips afterwards Build: A global hotkey writes a marker with the elapsed milliseconds and an optional label, working while another application has focus. After the meeting, each marker becomes a clip with configurable lead-in and lead-out, cut across segment boundaries. Clips are files in the meeting folder with a row in the database. Trap: Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. Done when: A marker dropped while another application has focus lands within a stated tolerance of the moment the key was pressed, tested by hand against a clock. A clip spanning two segments plays continuously with no gap or repeat, tested. A clip requested across a missing segment fails with a message naming the gap instead of producing silence. Clips are ordinary media files in the meeting folder and play in any player. Do not build yet: No library search, retention or export yet. ### Phase 5 · A library that forgets on purpose Build: A local library page listing meetings with search across transcripts and notes. Delete audio after a configured retention while keeping transcripts, notes and clips, with the deletion recorded. A per-meeting delete removes the folder, the rows, the clips and the search index together. Export a meeting as a folder of plain files that opens without this application. Trap: Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. Done when: Audio older than the retention period is deleted on schedule and the transcript, notes and clips survive with a recorded deletion time, tested by moving the clock. Deleting a meeting removes the folder, every row and the search index entry, verified by scanning the directory and querying the index afterwards. Search returns a meeting by a phrase spoken in it and links to the timestamp. An exported meeting folder contains audio, transcript, notes and clips as ordinary files and is readable with no application installed. Do not build yet: Nothing after this. ### Out of scope (and why) - A bot that joins a video call on your behalf, which records people who were told a person was attending and is the part of the paid product most likely to breach a platform's terms. - Speaker identification, which needs enrolment audio per person and a stored voice profile of your colleagues, a much heavier thing to hold than a transcript. - Customer record synchronisation, which exists so a sales team can file a conversation, and carries the same consent question into somebody else's database. - A shared team library, because the moment recordings leave your machine you are operating a retention policy for other people's voices. ### README must contain - That recording other people requires telling them, that the consent note is stored with each meeting, and that this build has no hidden capture and no bot - That the published model card for open speech models warns predictions can include text that was never spoken, which is why every summary bullet carries citations - That the recording is a folder of numbered segments, so a crash costs seconds rather than the meeting - The retention period for audio, what survives when it expires, and exactly what deleting a meeting removes - Which platform permissions are needed for microphone and system audio capture, and what the application does when it cannot get them ===== AGENTS.md ===== # Agent instructions · tl;dv indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Record to short numbered segment files on disk, never one growing buffer, A local speech model by default, a hosted one only if a key is present, One model call over the transcript, every bullet carrying the line ids behind it, SQLite index over a directory of meeting folders you can open in a file manager, Audio deleted on a schedule, transcript and notes kept. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ===== BUILD_PLAN.md ===== # Build plan · tl;dv A local recorder whose hard problems are capturing hours of audio without losing any of it, presenting a transcript that invents text as something the reader is expected to verify, and keeping a growing pile of recordings of other people under control. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Capture that survives a three hour call Recording writes continuously to disk in short segments and a mid-meeting failure costs seconds, not hours. ### Steps 1. Write numbered segments and close each one The meeting is the folder; the file is only the last few seconds at risk. Files: `src/capture/recorder.ts`, `tests/segment-crash.test.ts` 2. Find out what your platform will actually give you Capturing another application's audio is an operating system question with a different answer on each one, so check the current documentation for your version before designing around it. Files: `docs/audio-capture.md` 3. Ask for the consent note before the first byte Afterwards it is a form nobody fills in. Files: `src/capture/start.ts` ### Done when - [ ] A kill mid-recording loses at most one segment - [ ] System audio either works or is honestly reported missing - [ ] The consent note is required and stored - [ ] Recording refuses to start on a nearly full disk ### Watch out - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. ## Phase 2 · A transcript the reader can check Every transcript line carries its timestamps and its confidence signals, and the audio behind it plays on a click. ### Steps 1. Transcribe in overlapping chunks and keep timestamps A boundary in the middle of a sentence is where transcripts go wrong. Files: `src/transcribe/chunks.ts`, `tests/overlap.test.ts` 2. Cut the silence before the model sees it Silence is where invented sentences come from. Files: `src/transcribe/vad.ts` 3. Store the confidence signals and show them The reader deciding whether to trust a line needs the number. Files: `src/transcribe/store.ts` ### Done when - [ ] Every line has timestamps and confidence signals - [ ] Clicking a line plays the matching audio - [ ] Silence produces no transcript lines - [ ] Overlapping chunks do not duplicate words ### Watch out - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. ## Phase 3 · A summary that cites or says nothing Notes and action items are generated only with the transcript lines they came from attached. ### Steps 1. Ask for line ids with every bullet A claim without a source is not a note, it is a guess. Files: `src/summary/prompt.ts` 2. Verify the citations before display The model will cite lines that do not exist. Files: `src/summary/verify.ts`, `tests/citations.test.ts` 3. Store model and prompt version with the summary So a strange bullet can be traced rather than argued about. Files: `src/summary/store.ts` ### Done when - [ ] Bullets with bad citations are dropped - [ ] Every bullet links to playable audio - [ ] No action items are invented from small talk - [ ] Model and prompt version are stored ### Watch out - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. ## Phase 4 · Markers while it happens, clips afterwards A hotkey during the meeting marks a moment, and each marker becomes a clip you can play or hand over. ### Steps 1. Register a hotkey that works without focus The moment worth clipping happens while you are looking at the call. Files: `src/capture/hotkey.ts` 2. Cut clips from the segment timeline Segment start and duration are the only reliable clock here. Files: `src/clips/cut.ts`, `tests/clip-boundary.test.ts` 3. Fail loudly across a gap A silent clip is a bug report you will never receive. Files: `src/clips/cut.ts` ### Done when - [ ] Markers land within tolerance without focus - [ ] Clips across segment boundaries play continuously - [ ] A clip across a gap fails with a clear message - [ ] Clips are ordinary files that play anywhere ### Watch out - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ## Not in this build - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. ## After v1, if you want it - A per-meeting cost and time report, so you know what a three hour recording actually costs to process - Transcript correction that feeds back into the summary, since the corrections are the only ground truth you will have - A weekly digest of your own action items, generated locally and never sent to anybody else ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. One folder per meeting holding segments, transcript, notes and clips. MEETINGS_DIR=~/Meetings # Required. The index over the meeting folders. Rebuildable from them. DATABASE_PATH=./data/meetings.db # Required. Segment length. This is how much a crash can cost you. SEGMENT_SECONDS=60 # Required. The local speech model. Its identifier is stored with every transcript line. ASR_MODEL_PATH=./models/speech-medium.bin # Optional · secret. Optional hosted model for summaries. Without it the transcript and clips still work. SUMMARY_API_KEY=sk-...redacted # Required. Audio is deleted after this. Transcripts, notes and clips survive. AUDIO_RETENTION_DAYS=30 # Required. Lead-in and lead-out either side of a marker when cutting a clip. CLIP_LEAD_SECONDS=30
You are building a production product version of tl;dv. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # tl;dv · product brief ## Problem A DIY app can record, transcribe, summarize, and generate clips, but tl;dv's team library, CRM integrations, and meeting-bot reliability are real work. ## Product outcome A recorder that tells people it is recording, loses at most a segment to a crash, never shows a summary bullet it cannot source, and deletes the audio on a schedule while keeping the notes. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - meeting platform integration - speech-to-text - video storage - LLM API - hosted backend ## Explicit non-goals for v1 - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. - meeting bot upkeep - team/shared library - CRM integrations - admin controls - polished clip sharing ## Success criteria - A kill mid-recording is confirmed to lose at most one segment - Silence is confirmed to produce no transcript lines - Summary bullets with bad citations are confirmed dropped - Audio expiry is confirmed to leave transcripts and notes intact - Deleting a meeting is confirmed to leave nothing on disk or in the index ===== BRIEF.md ===== # Build brief · tl;dv The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local meeting recorder that transcribes what you were in the room for and keeps every file on your own machine to replace tl;dv. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Record only conversations you are part of, and say out loud that you are recording before you start. This build has no bot that joins a call on your behalf, no hidden capture, and no indicator you can switch off. The published model card for the open transcription models most people use for this says in as many words that they should not be used to transcribe recordings of individuals taken without their consent, and that is the rule here regardless of what the law where you live allows. Store the consent note with the meeting: who was told, and how. The second rule follows from the same model card, which states that predictions may include text that was not spoken in the audio at all. Speech recognition invents fluent sentences over silence, music and crosstalk, and a summary built on those sentences reads beautifully and is false. So no summary bullet exists in this application without the transcript lines it came from and a timestamp you can click to hear the audio. If a bullet cannot cite, it does not get displayed. Do not build a feature that mails a summary to other people, because you will not be there to check it. ### Stack (fixed, do not substitute) - Capture: Record to short numbered segment files on disk, never one growing buffer · a three hour meeting that dies at minute one hundred and seventy should cost you one segment - Transcription: A local speech model by default, a hosted one only if a key is present · audio of other people talking is the last thing that should leave the machine by default - Summary: One model call over the transcript, every bullet carrying the line ids behind it · the published model card warns the transcript can contain text nobody said - Library: SQLite index over a directory of meeting folders you can open in a file manager · the recordings should outlive this application - Retention: Audio deleted on a schedule, transcript and notes kept · a recording of a colleague is a liability with a shelf life ### Data model (create this before Phase 1) - meetings(id, title, started_at, ended_at, folder, consent_note, state) · consent_note records who was told and how - segments(id, meeting_id, ordinal, path, started_at, seconds, bytes) · the recording is a sequence of short files, not one buffer - lines(id, meeting_id, start_ms, end_ms, text, no_speech_prob, avg_logprob, model_id, chunk_ordinal) - markers(id, meeting_id, at_ms, label, created_at) · dropped live with a hotkey while the meeting runs - clips(id, meeting_id, marker_id, start_ms, end_ms, path, created_at) - summaries(id, meeting_id, model_id, prompt_version, created_at, bullets_json) · each bullet holds its text and the line ids it cites - retention(meeting_id, audio_delete_after, audio_deleted_at) · the transcript survives the audio - There is no column anywhere holding a summary sentence without a list of line ids beside it. A bullet that cannot cite is a bug. ### Phase 1 · Capture that survives a three hour call Build: Start and stop from a single control or command. Write fixed length segments, numbered, into a meeting folder, flushing each one as it closes. Show elapsed time, input level and a plainly visible recording state. Prompt for the consent note before the first segment is written, and store it with the meeting. Trap: Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. Done when: Killing the process mid-recording leaves every closed segment playable and loses at most the current one, tested by killing it twice at different points. Capturing system audio as well as the microphone is either working on your platform or the application says clearly that it is not and records the microphone only. The consent note is required before recording starts and is stored with the meeting, tested. Disk space is checked before starting and recording refuses to begin below a stated threshold. Do not build yet: No transcription, summary, clips or library yet. ### Phase 2 · A transcript the reader can check Build: Transcribe in overlapping chunks so no word falls on a boundary, keeping the model's own timestamps. Store per line the probability that the segment held no speech and the average confidence. Detect long silences before transcribing and skip them. Render the transcript with each line clickable to play the audio from that moment. Trap: Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. Done when: Every stored line has start and end milliseconds, a no-speech probability and an average confidence, checked in the database. Clicking any line plays the audio at that timestamp, within a stated tolerance, tested on a recording spanning several segments. A recording of pure silence produces no transcript lines at all, tested with a generated file. Chunk boundaries overlap and a word spoken across a boundary appears once, tested with a fixture. Do not build yet: No summary, markers, clips or retention yet. ### Phase 3 · A summary that cites or says nothing Build: One model call over the transcript asking for bullets and action items, each with the line ids it used. Drop any bullet whose cited lines do not exist or whose text does not appear near them. Render each bullet with its timestamps. Show the whole summary as unverified until a person has opened at least the action items. Trap: A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. Done when: A bullet whose cited line ids do not exist is dropped before display, tested with a stub returning invented ids. Every displayed bullet links to a timestamp that plays the audio. A transcript with no action items produces an empty action list rather than invented ones, tested with a fixture of small talk. The model identifier and prompt version are stored with every summary. Do not build yet: No markers, clips, search or retention yet. ### Phase 4 · Markers while it happens, clips afterwards Build: A global hotkey writes a marker with the elapsed milliseconds and an optional label, working while another application has focus. After the meeting, each marker becomes a clip with configurable lead-in and lead-out, cut across segment boundaries. Clips are files in the meeting folder with a row in the database. Trap: Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. Done when: A marker dropped while another application has focus lands within a stated tolerance of the moment the key was pressed, tested by hand against a clock. A clip spanning two segments plays continuously with no gap or repeat, tested. A clip requested across a missing segment fails with a message naming the gap instead of producing silence. Clips are ordinary media files in the meeting folder and play in any player. Do not build yet: No library search, retention or export yet. ### Phase 5 · A library that forgets on purpose Build: A local library page listing meetings with search across transcripts and notes. Delete audio after a configured retention while keeping transcripts, notes and clips, with the deletion recorded. A per-meeting delete removes the folder, the rows, the clips and the search index together. Export a meeting as a folder of plain files that opens without this application. Trap: Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. Done when: Audio older than the retention period is deleted on schedule and the transcript, notes and clips survive with a recorded deletion time, tested by moving the clock. Deleting a meeting removes the folder, every row and the search index entry, verified by scanning the directory and querying the index afterwards. Search returns a meeting by a phrase spoken in it and links to the timestamp. An exported meeting folder contains audio, transcript, notes and clips as ordinary files and is readable with no application installed. Do not build yet: Nothing after this. ### Out of scope (and why) - A bot that joins a video call on your behalf, which records people who were told a person was attending and is the part of the paid product most likely to breach a platform's terms. - Speaker identification, which needs enrolment audio per person and a stored voice profile of your colleagues, a much heavier thing to hold than a transcript. - Customer record synchronisation, which exists so a sales team can file a conversation, and carries the same consent question into somebody else's database. - A shared team library, because the moment recordings leave your machine you are operating a retention policy for other people's voices. ### README must contain - That recording other people requires telling them, that the consent note is stored with each meeting, and that this build has no hidden capture and no bot - That the published model card for open speech models warns predictions can include text that was never spoken, which is why every summary bullet carries citations - That the recording is a folder of numbered segments, so a crash costs seconds rather than the meeting - The retention period for audio, what survives when it expires, and exactly what deleting a meeting removes - Which platform permissions are needed for microphone and system audio capture, and what the application does when it cannot get them ===== ARCHITECTURE.md ===== # Architecture · tl;dv ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Record to short numbered segment files on disk, never one growing buffer | a three hour meeting that dies at minute one hundred and seventy should cost you one segment | | Transcription | A local speech model by default, a hosted one only if a key is present | audio of other people talking is the last thing that should leave the machine by default | | Summary | One model call over the transcript, every bullet carrying the line ids behind it | the published model card warns the transcript can contain text nobody said | | Library | SQLite index over a directory of meeting folders you can open in a file manager | the recordings should outlive this application | | Retention | Audio deleted on a schedule, transcript and notes kept | a recording of a colleague is a liability with a shelf life | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capture | Segmented recording and the consent note. | Any recorder, provided it writes closed segments rather than one file. | | Transcription | Chunking, silence removal, confidence signals. | Any model, provided timestamps and a no-speech signal survive. | | Summary | Citation-carrying bullets and verification. | Any provider, provided uncited bullets are dropped. | | Clips | Markers and cutting from the segment timeline. | Any cutter, provided a gap fails loudly. | | Retention | Audio expiry and real deletion. | Not swappable for keeping everything forever. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `MEETINGS_DIR` · required · One folder per meeting holding segments, transcript, notes and clips. - `DATABASE_PATH` · required · The index over the meeting folders. Rebuildable from them. - `SEGMENT_SECONDS` · required · Segment length. This is how much a crash can cost you. - `ASR_MODEL_PATH` · required · The local speech model. Its identifier is stored with every transcript line. - `SUMMARY_API_KEY` · optional, secret · Optional hosted model for summaries. Without it the transcript and clips still work. - `AUDIO_RETENTION_DAYS` · required · Audio is deleted after this. Transcripts, notes and clips survive. - `CLIP_LEAD_SECONDS` · required · Lead-in and lead-out either side of a marker when cutting a clip. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · tl;dv product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Record to short numbered segment files on disk, never one growing buffer, A local speech model by default, a hosted one only if a key is present, One model call over the transcript, every bullet carrying the line ids behind it, SQLite index over a directory of meeting folders you can open in a file manager, Audio deleted on a schedule, transcript and notes kept. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. - Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. ===== MILESTONES.md ===== # Delivery milestones · tl;dv Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Capture that survives a three hour call Recording writes continuously to disk in short segments and a mid-meeting failure costs seconds, not hours. ### Steps 1. Write numbered segments and close each one The meeting is the folder; the file is only the last few seconds at risk. Files: `src/capture/recorder.ts`, `tests/segment-crash.test.ts` 2. Find out what your platform will actually give you Capturing another application's audio is an operating system question with a different answer on each one, so check the current documentation for your version before designing around it. Files: `docs/audio-capture.md` 3. Ask for the consent note before the first byte Afterwards it is a form nobody fills in. Files: `src/capture/start.ts` ### Done when - [ ] A kill mid-recording loses at most one segment - [ ] System audio either works or is honestly reported missing - [ ] The consent note is required and stored - [ ] Recording refuses to start on a nearly full disk ### Watch out - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. ## M2 · A transcript the reader can check Every transcript line carries its timestamps and its confidence signals, and the audio behind it plays on a click. ### Steps 1. Transcribe in overlapping chunks and keep timestamps A boundary in the middle of a sentence is where transcripts go wrong. Files: `src/transcribe/chunks.ts`, `tests/overlap.test.ts` 2. Cut the silence before the model sees it Silence is where invented sentences come from. Files: `src/transcribe/vad.ts` 3. Store the confidence signals and show them The reader deciding whether to trust a line needs the number. Files: `src/transcribe/store.ts` ### Done when - [ ] Every line has timestamps and confidence signals - [ ] Clicking a line plays the matching audio - [ ] Silence produces no transcript lines - [ ] Overlapping chunks do not duplicate words ### Watch out - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. ## M3 · A summary that cites or says nothing Notes and action items are generated only with the transcript lines they came from attached. ### Steps 1. Ask for line ids with every bullet A claim without a source is not a note, it is a guess. Files: `src/summary/prompt.ts` 2. Verify the citations before display The model will cite lines that do not exist. Files: `src/summary/verify.ts`, `tests/citations.test.ts` 3. Store model and prompt version with the summary So a strange bullet can be traced rather than argued about. Files: `src/summary/store.ts` ### Done when - [ ] Bullets with bad citations are dropped - [ ] Every bullet links to playable audio - [ ] No action items are invented from small talk - [ ] Model and prompt version are stored ### Watch out - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. ## M4 · Markers while it happens, clips afterwards A hotkey during the meeting marks a moment, and each marker becomes a clip you can play or hand over. ### Steps 1. Register a hotkey that works without focus The moment worth clipping happens while you are looking at the call. Files: `src/capture/hotkey.ts` 2. Cut clips from the segment timeline Segment start and duration are the only reliable clock here. Files: `src/clips/cut.ts`, `tests/clip-boundary.test.ts` 3. Fail loudly across a gap A silent clip is a bug report you will never receive. Files: `src/clips/cut.ts` ### Done when - [ ] Markers land within tolerance without focus - [ ] Clips across segment boundaries play continuously - [ ] A clip across a gap fails with a clear message - [ ] Clips are ordinary files that play anywhere ### Watch out - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ## M5 · A library that forgets on purpose (production only) Meetings are searchable, the audio expires on a schedule, and deleting a meeting really removes it. ### Steps 1. Expire the audio, keep the notes The notes were the point; the recording was the means. Files: `src/retention/sweep.ts`, `tests/retention.test.ts` 2. Make deletion take the folder and the index Anything left behind makes your deletion claim untrue. Files: `src/meetings/delete.ts` 3. Export as plain files anyone can open This tool should not be needed to read your own meetings. Files: `src/export/folder.ts` ```sh npm run export -- --meeting=2026-09-15-standup ``` ### Done when - [ ] Audio expires on schedule and notes survive - [ ] Deleting a meeting leaves nothing on disk or in the index - [ ] Search finds a spoken phrase and links to its timestamp - [ ] An exported folder opens without this application ### Watch out - Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. ===== OPERATIONS.md ===== # Operations · tl;dv ## Backup Back up the meeting folders, not the index, since the index rebuilds from them. Think carefully before backing up audio of other people to anywhere you do not control, and prefer backing up transcripts and notes only. ## Restore Restore the folders, reindex, and confirm the meeting count and that a random transcript line still plays its audio. If audio was deliberately excluded, the transcripts should restore alone without errors. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Free disk against the retention period, the proportion of transcript lines below your confidence threshold, and the number of summary bullets dropped for bad citations. A rising drop rate usually means the transcript quality fell, not that the summary model changed. ## Incident checklist The incident here is a person asking what you have of them and when it will be gone. The consent note, the retention setting and the recorded audio deletion times are the answer, so keep them accurate rather than aspirational. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A kill mid-recording is confirmed to lose at most one segment - [ ] Silence is confirmed to produce no transcript lines - [ ] Summary bullets with bad citations are confirmed dropped - [ ] Audio expiry is confirmed to leave transcripts and notes intact - [ ] Deleting a meeting is confirmed to leave nothing on disk or in the index ## Launch constraint Do not market omitted tl;dv capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. One folder per meeting holding segments, transcript, notes and clips. MEETINGS_DIR=~/Meetings # Required. The index over the meeting folders. Rebuildable from them. DATABASE_PATH=./data/meetings.db # Required. Segment length. This is how much a crash can cost you. SEGMENT_SECONDS=60 # Required. The local speech model. Its identifier is stored with every transcript line. ASR_MODEL_PATH=./models/speech-medium.bin # Optional · secret. Optional hosted model for summaries. Without it the transcript and clips still work. SUMMARY_API_KEY=sk-...redacted # Required. Audio is deleted after this. Transcripts, notes and clips survive. AUDIO_RETENTION_DAYS=30 # Required. Lead-in and lead-out either side of a marker when cutting a clip. CLIP_LEAD_SECONDS=30
# tl;dv · indie build A local recorder whose hard problems are capturing hours of audio without losing any of it, presenting a transcript that invents text as something the reader is expected to verify, and keeping a growing pile of recordings of other people under control. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Record to short numbered segment files on disk, never one growing buffer | a three hour meeting that dies at minute one hundred and seventy should cost you one segment | | Transcription | A local speech model by default, a hosted one only if a key is present | audio of other people talking is the last thing that should leave the machine by default | | Summary | One model call over the transcript, every bullet carrying the line ids behind it | the published model card warns the transcript can contain text nobody said | | Library | SQLite index over a directory of meeting folders you can open in a file manager | the recordings should outlive this application | | Retention | Audio deleted on a schedule, transcript and notes kept | a recording of a colleague is a liability with a shelf life | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the recorder, the library and the workers. - Get it: nodejs.org, or your version manager. - Verify: node --version prints v22 or higher - [ ] **ffmpeg** · free - Why: Segment recording, clip cutting and format conversion all go through it, and the clip boundary work depends on its behaviour. - Get it: Your package manager, or the official builds. - Verify: ffmpeg -version prints a version and the build lists your audio input device - [ ] **A local speech recognition model and runtime** · free, plus disk for the model - Why: The default path keeps audio of other people on your machine, and the model size you choose decides both accuracy and how long a meeting takes to process. - Get it: Download a runtime and one model file, and note which model you used. - Verify: A two minute recording transcribes with timestamps in a time you are willing to wait - [ ] **A working system audio capture route on your platform** · free, or the price of a virtual device - Why: Recording the other side of a call is the part that differs most between operating systems and versions, and it decides whether this tool is useful or just records you. - Get it: Check the current documentation for your operating system version, then either use its supported capture interface or install a virtual audio device. - Verify: A test recording contains both your voice and audio played by another application - [ ] **What you will say before recording, and to whom** · free - Why: The rule here is that you announce it, and deciding the wording once means you actually do it rather than deciding in the moment. - Get it: Write the sentence you will say and the retention period you will offer, and put both in the README. - Verify: The consent prompt in the application matches what the README promises ## Quick start ```sh npm run export -- --meeting=2026-09-15-standup ``` Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. - meeting bot upkeep - team/shared library - CRM integrations - admin controls - polished clip sharing If one of those is essential to you, that is the reason to keep paying for tl;dv, and the README should say so rather than pretend.
# Build brief · tl;dv The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local meeting recorder that transcribes what you were in the room for and keeps every file on your own machine to replace tl;dv. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Record only conversations you are part of, and say out loud that you are recording before you start. This build has no bot that joins a call on your behalf, no hidden capture, and no indicator you can switch off. The published model card for the open transcription models most people use for this says in as many words that they should not be used to transcribe recordings of individuals taken without their consent, and that is the rule here regardless of what the law where you live allows. Store the consent note with the meeting: who was told, and how. The second rule follows from the same model card, which states that predictions may include text that was not spoken in the audio at all. Speech recognition invents fluent sentences over silence, music and crosstalk, and a summary built on those sentences reads beautifully and is false. So no summary bullet exists in this application without the transcript lines it came from and a timestamp you can click to hear the audio. If a bullet cannot cite, it does not get displayed. Do not build a feature that mails a summary to other people, because you will not be there to check it. ### Stack (fixed, do not substitute) - Capture: Record to short numbered segment files on disk, never one growing buffer · a three hour meeting that dies at minute one hundred and seventy should cost you one segment - Transcription: A local speech model by default, a hosted one only if a key is present · audio of other people talking is the last thing that should leave the machine by default - Summary: One model call over the transcript, every bullet carrying the line ids behind it · the published model card warns the transcript can contain text nobody said - Library: SQLite index over a directory of meeting folders you can open in a file manager · the recordings should outlive this application - Retention: Audio deleted on a schedule, transcript and notes kept · a recording of a colleague is a liability with a shelf life ### Data model (create this before Phase 1) - meetings(id, title, started_at, ended_at, folder, consent_note, state) · consent_note records who was told and how - segments(id, meeting_id, ordinal, path, started_at, seconds, bytes) · the recording is a sequence of short files, not one buffer - lines(id, meeting_id, start_ms, end_ms, text, no_speech_prob, avg_logprob, model_id, chunk_ordinal) - markers(id, meeting_id, at_ms, label, created_at) · dropped live with a hotkey while the meeting runs - clips(id, meeting_id, marker_id, start_ms, end_ms, path, created_at) - summaries(id, meeting_id, model_id, prompt_version, created_at, bullets_json) · each bullet holds its text and the line ids it cites - retention(meeting_id, audio_delete_after, audio_deleted_at) · the transcript survives the audio - There is no column anywhere holding a summary sentence without a list of line ids beside it. A bullet that cannot cite is a bug. ### Phase 1 · Capture that survives a three hour call Build: Start and stop from a single control or command. Write fixed length segments, numbered, into a meeting folder, flushing each one as it closes. Show elapsed time, input level and a plainly visible recording state. Prompt for the consent note before the first segment is written, and store it with the meeting. Trap: Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. Done when: Killing the process mid-recording leaves every closed segment playable and loses at most the current one, tested by killing it twice at different points. Capturing system audio as well as the microphone is either working on your platform or the application says clearly that it is not and records the microphone only. The consent note is required before recording starts and is stored with the meeting, tested. Disk space is checked before starting and recording refuses to begin below a stated threshold. Do not build yet: No transcription, summary, clips or library yet. ### Phase 2 · A transcript the reader can check Build: Transcribe in overlapping chunks so no word falls on a boundary, keeping the model's own timestamps. Store per line the probability that the segment held no speech and the average confidence. Detect long silences before transcribing and skip them. Render the transcript with each line clickable to play the audio from that moment. Trap: Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. Done when: Every stored line has start and end milliseconds, a no-speech probability and an average confidence, checked in the database. Clicking any line plays the audio at that timestamp, within a stated tolerance, tested on a recording spanning several segments. A recording of pure silence produces no transcript lines at all, tested with a generated file. Chunk boundaries overlap and a word spoken across a boundary appears once, tested with a fixture. Do not build yet: No summary, markers, clips or retention yet. ### Phase 3 · A summary that cites or says nothing Build: One model call over the transcript asking for bullets and action items, each with the line ids it used. Drop any bullet whose cited lines do not exist or whose text does not appear near them. Render each bullet with its timestamps. Show the whole summary as unverified until a person has opened at least the action items. Trap: A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. Done when: A bullet whose cited line ids do not exist is dropped before display, tested with a stub returning invented ids. Every displayed bullet links to a timestamp that plays the audio. A transcript with no action items produces an empty action list rather than invented ones, tested with a fixture of small talk. The model identifier and prompt version are stored with every summary. Do not build yet: No markers, clips, search or retention yet. ### Phase 4 · Markers while it happens, clips afterwards Build: A global hotkey writes a marker with the elapsed milliseconds and an optional label, working while another application has focus. After the meeting, each marker becomes a clip with configurable lead-in and lead-out, cut across segment boundaries. Clips are files in the meeting folder with a row in the database. Trap: Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. Done when: A marker dropped while another application has focus lands within a stated tolerance of the moment the key was pressed, tested by hand against a clock. A clip spanning two segments plays continuously with no gap or repeat, tested. A clip requested across a missing segment fails with a message naming the gap instead of producing silence. Clips are ordinary media files in the meeting folder and play in any player. Do not build yet: No library search, retention or export yet. ### Phase 5 · A library that forgets on purpose Build: A local library page listing meetings with search across transcripts and notes. Delete audio after a configured retention while keeping transcripts, notes and clips, with the deletion recorded. A per-meeting delete removes the folder, the rows, the clips and the search index together. Export a meeting as a folder of plain files that opens without this application. Trap: Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. Done when: Audio older than the retention period is deleted on schedule and the transcript, notes and clips survive with a recorded deletion time, tested by moving the clock. Deleting a meeting removes the folder, every row and the search index entry, verified by scanning the directory and querying the index afterwards. Search returns a meeting by a phrase spoken in it and links to the timestamp. An exported meeting folder contains audio, transcript, notes and clips as ordinary files and is readable with no application installed. Do not build yet: Nothing after this. ### Out of scope (and why) - A bot that joins a video call on your behalf, which records people who were told a person was attending and is the part of the paid product most likely to breach a platform's terms. - Speaker identification, which needs enrolment audio per person and a stored voice profile of your colleagues, a much heavier thing to hold than a transcript. - Customer record synchronisation, which exists so a sales team can file a conversation, and carries the same consent question into somebody else's database. - A shared team library, because the moment recordings leave your machine you are operating a retention policy for other people's voices. ### README must contain - That recording other people requires telling them, that the consent note is stored with each meeting, and that this build has no hidden capture and no bot - That the published model card for open speech models warns predictions can include text that was never spoken, which is why every summary bullet carries citations - That the recording is a folder of numbered segments, so a crash costs seconds rather than the meeting - The retention period for audio, what survives when it expires, and exactly what deleting a meeting removes - Which platform permissions are needed for microphone and system audio capture, and what the application does when it cannot get them
# Agent instructions · tl;dv indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Record to short numbered segment files on disk, never one growing buffer, A local speech model by default, a hosted one only if a key is present, One model call over the transcript, every bullet carrying the line ids behind it, SQLite index over a directory of meeting folders you can open in a file manager, Audio deleted on a schedule, transcript and notes kept. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap.
# Build plan · tl;dv A local recorder whose hard problems are capturing hours of audio without losing any of it, presenting a transcript that invents text as something the reader is expected to verify, and keeping a growing pile of recordings of other people under control. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Capture that survives a three hour call Recording writes continuously to disk in short segments and a mid-meeting failure costs seconds, not hours. ### Steps 1. Write numbered segments and close each one The meeting is the folder; the file is only the last few seconds at risk. Files: `src/capture/recorder.ts`, `tests/segment-crash.test.ts` 2. Find out what your platform will actually give you Capturing another application's audio is an operating system question with a different answer on each one, so check the current documentation for your version before designing around it. Files: `docs/audio-capture.md` 3. Ask for the consent note before the first byte Afterwards it is a form nobody fills in. Files: `src/capture/start.ts` ### Done when - [ ] A kill mid-recording loses at most one segment - [ ] System audio either works or is honestly reported missing - [ ] The consent note is required and stored - [ ] Recording refuses to start on a nearly full disk ### Watch out - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. ## Phase 2 · A transcript the reader can check Every transcript line carries its timestamps and its confidence signals, and the audio behind it plays on a click. ### Steps 1. Transcribe in overlapping chunks and keep timestamps A boundary in the middle of a sentence is where transcripts go wrong. Files: `src/transcribe/chunks.ts`, `tests/overlap.test.ts` 2. Cut the silence before the model sees it Silence is where invented sentences come from. Files: `src/transcribe/vad.ts` 3. Store the confidence signals and show them The reader deciding whether to trust a line needs the number. Files: `src/transcribe/store.ts` ### Done when - [ ] Every line has timestamps and confidence signals - [ ] Clicking a line plays the matching audio - [ ] Silence produces no transcript lines - [ ] Overlapping chunks do not duplicate words ### Watch out - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. ## Phase 3 · A summary that cites or says nothing Notes and action items are generated only with the transcript lines they came from attached. ### Steps 1. Ask for line ids with every bullet A claim without a source is not a note, it is a guess. Files: `src/summary/prompt.ts` 2. Verify the citations before display The model will cite lines that do not exist. Files: `src/summary/verify.ts`, `tests/citations.test.ts` 3. Store model and prompt version with the summary So a strange bullet can be traced rather than argued about. Files: `src/summary/store.ts` ### Done when - [ ] Bullets with bad citations are dropped - [ ] Every bullet links to playable audio - [ ] No action items are invented from small talk - [ ] Model and prompt version are stored ### Watch out - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. ## Phase 4 · Markers while it happens, clips afterwards A hotkey during the meeting marks a moment, and each marker becomes a clip you can play or hand over. ### Steps 1. Register a hotkey that works without focus The moment worth clipping happens while you are looking at the call. Files: `src/capture/hotkey.ts` 2. Cut clips from the segment timeline Segment start and duration are the only reliable clock here. Files: `src/clips/cut.ts`, `tests/clip-boundary.test.ts` 3. Fail loudly across a gap A silent clip is a bug report you will never receive. Files: `src/clips/cut.ts` ### Done when - [ ] Markers land within tolerance without focus - [ ] Clips across segment boundaries play continuously - [ ] A clip across a gap fails with a clear message - [ ] Clips are ordinary files that play anywhere ### Watch out - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ## Not in this build - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. ## After v1, if you want it - A per-meeting cost and time report, so you know what a three hour recording actually costs to process - Transcript correction that feeds back into the summary, since the corrections are the only ground truth you will have - A weekly digest of your own action items, generated locally and never sent to anybody else
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. One folder per meeting holding segments, transcript, notes and clips. MEETINGS_DIR=~/Meetings # Required. The index over the meeting folders. Rebuildable from them. DATABASE_PATH=./data/meetings.db # Required. Segment length. This is how much a crash can cost you. SEGMENT_SECONDS=60 # Required. The local speech model. Its identifier is stored with every transcript line. ASR_MODEL_PATH=./models/speech-medium.bin # Optional · secret. Optional hosted model for summaries. Without it the transcript and clips still work. SUMMARY_API_KEY=sk-...redacted # Required. Audio is deleted after this. Transcripts, notes and clips survive. AUDIO_RETENTION_DAYS=30 # Required. Lead-in and lead-out either side of a marker when cutting a clip. CLIP_LEAD_SECONDS=30
# tl;dv · product brief ## Problem A DIY app can record, transcribe, summarize, and generate clips, but tl;dv's team library, CRM integrations, and meeting-bot reliability are real work. ## Product outcome A recorder that tells people it is recording, loses at most a segment to a crash, never shows a summary bullet it cannot source, and deletes the audio on a schedule while keeping the notes. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - meeting platform integration - speech-to-text - video storage - LLM API - hosted backend ## Explicit non-goals for v1 - A bot that joins calls on your behalf. - Speaker identification and stored voice profiles. - Customer record synchronisation. - A shared team library. - meeting bot upkeep - team/shared library - CRM integrations - admin controls - polished clip sharing ## Success criteria - A kill mid-recording is confirmed to lose at most one segment - Silence is confirmed to produce no transcript lines - Summary bullets with bad citations are confirmed dropped - Audio expiry is confirmed to leave transcripts and notes intact - Deleting a meeting is confirmed to leave nothing on disk or in the index
# Build brief · tl;dv The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local meeting recorder that transcribes what you were in the room for and keeps every file on your own machine to replace tl;dv. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Record only conversations you are part of, and say out loud that you are recording before you start. This build has no bot that joins a call on your behalf, no hidden capture, and no indicator you can switch off. The published model card for the open transcription models most people use for this says in as many words that they should not be used to transcribe recordings of individuals taken without their consent, and that is the rule here regardless of what the law where you live allows. Store the consent note with the meeting: who was told, and how. The second rule follows from the same model card, which states that predictions may include text that was not spoken in the audio at all. Speech recognition invents fluent sentences over silence, music and crosstalk, and a summary built on those sentences reads beautifully and is false. So no summary bullet exists in this application without the transcript lines it came from and a timestamp you can click to hear the audio. If a bullet cannot cite, it does not get displayed. Do not build a feature that mails a summary to other people, because you will not be there to check it. ### Stack (fixed, do not substitute) - Capture: Record to short numbered segment files on disk, never one growing buffer · a three hour meeting that dies at minute one hundred and seventy should cost you one segment - Transcription: A local speech model by default, a hosted one only if a key is present · audio of other people talking is the last thing that should leave the machine by default - Summary: One model call over the transcript, every bullet carrying the line ids behind it · the published model card warns the transcript can contain text nobody said - Library: SQLite index over a directory of meeting folders you can open in a file manager · the recordings should outlive this application - Retention: Audio deleted on a schedule, transcript and notes kept · a recording of a colleague is a liability with a shelf life ### Data model (create this before Phase 1) - meetings(id, title, started_at, ended_at, folder, consent_note, state) · consent_note records who was told and how - segments(id, meeting_id, ordinal, path, started_at, seconds, bytes) · the recording is a sequence of short files, not one buffer - lines(id, meeting_id, start_ms, end_ms, text, no_speech_prob, avg_logprob, model_id, chunk_ordinal) - markers(id, meeting_id, at_ms, label, created_at) · dropped live with a hotkey while the meeting runs - clips(id, meeting_id, marker_id, start_ms, end_ms, path, created_at) - summaries(id, meeting_id, model_id, prompt_version, created_at, bullets_json) · each bullet holds its text and the line ids it cites - retention(meeting_id, audio_delete_after, audio_deleted_at) · the transcript survives the audio - There is no column anywhere holding a summary sentence without a list of line ids beside it. A bullet that cannot cite is a bug. ### Phase 1 · Capture that survives a three hour call Build: Start and stop from a single control or command. Write fixed length segments, numbered, into a meeting folder, flushing each one as it closes. Show elapsed time, input level and a plainly visible recording state. Prompt for the consent note before the first segment is written, and store it with the meeting. Trap: Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. Done when: Killing the process mid-recording leaves every closed segment playable and loses at most the current one, tested by killing it twice at different points. Capturing system audio as well as the microphone is either working on your platform or the application says clearly that it is not and records the microphone only. The consent note is required before recording starts and is stored with the meeting, tested. Disk space is checked before starting and recording refuses to begin below a stated threshold. Do not build yet: No transcription, summary, clips or library yet. ### Phase 2 · A transcript the reader can check Build: Transcribe in overlapping chunks so no word falls on a boundary, keeping the model's own timestamps. Store per line the probability that the segment held no speech and the average confidence. Detect long silences before transcribing and skip them. Render the transcript with each line clickable to play the audio from that moment. Trap: Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. Done when: Every stored line has start and end milliseconds, a no-speech probability and an average confidence, checked in the database. Clicking any line plays the audio at that timestamp, within a stated tolerance, tested on a recording spanning several segments. A recording of pure silence produces no transcript lines at all, tested with a generated file. Chunk boundaries overlap and a word spoken across a boundary appears once, tested with a fixture. Do not build yet: No summary, markers, clips or retention yet. ### Phase 3 · A summary that cites or says nothing Build: One model call over the transcript asking for bullets and action items, each with the line ids it used. Drop any bullet whose cited lines do not exist or whose text does not appear near them. Render each bullet with its timestamps. Show the whole summary as unverified until a person has opened at least the action items. Trap: A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. Done when: A bullet whose cited line ids do not exist is dropped before display, tested with a stub returning invented ids. Every displayed bullet links to a timestamp that plays the audio. A transcript with no action items produces an empty action list rather than invented ones, tested with a fixture of small talk. The model identifier and prompt version are stored with every summary. Do not build yet: No markers, clips, search or retention yet. ### Phase 4 · Markers while it happens, clips afterwards Build: A global hotkey writes a marker with the elapsed milliseconds and an optional label, working while another application has focus. After the meeting, each marker becomes a clip with configurable lead-in and lead-out, cut across segment boundaries. Clips are files in the meeting folder with a row in the database. Trap: Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. Done when: A marker dropped while another application has focus lands within a stated tolerance of the moment the key was pressed, tested by hand against a clock. A clip spanning two segments plays continuously with no gap or repeat, tested. A clip requested across a missing segment fails with a message naming the gap instead of producing silence. Clips are ordinary media files in the meeting folder and play in any player. Do not build yet: No library search, retention or export yet. ### Phase 5 · A library that forgets on purpose Build: A local library page listing meetings with search across transcripts and notes. Delete audio after a configured retention while keeping transcripts, notes and clips, with the deletion recorded. A per-meeting delete removes the folder, the rows, the clips and the search index together. Export a meeting as a folder of plain files that opens without this application. Trap: Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point. Done when: Audio older than the retention period is deleted on schedule and the transcript, notes and clips survive with a recorded deletion time, tested by moving the clock. Deleting a meeting removes the folder, every row and the search index entry, verified by scanning the directory and querying the index afterwards. Search returns a meeting by a phrase spoken in it and links to the timestamp. An exported meeting folder contains audio, transcript, notes and clips as ordinary files and is readable with no application installed. Do not build yet: Nothing after this. ### Out of scope (and why) - A bot that joins a video call on your behalf, which records people who were told a person was attending and is the part of the paid product most likely to breach a platform's terms. - Speaker identification, which needs enrolment audio per person and a stored voice profile of your colleagues, a much heavier thing to hold than a transcript. - Customer record synchronisation, which exists so a sales team can file a conversation, and carries the same consent question into somebody else's database. - A shared team library, because the moment recordings leave your machine you are operating a retention policy for other people's voices. ### README must contain - That recording other people requires telling them, that the consent note is stored with each meeting, and that this build has no hidden capture and no bot - That the published model card for open speech models warns predictions can include text that was never spoken, which is why every summary bullet carries citations - That the recording is a folder of numbered segments, so a crash costs seconds rather than the meeting - The retention period for audio, what survives when it expires, and exactly what deleting a meeting removes - Which platform permissions are needed for microphone and system audio capture, and what the application does when it cannot get them
# Architecture · tl;dv ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Record to short numbered segment files on disk, never one growing buffer | a three hour meeting that dies at minute one hundred and seventy should cost you one segment | | Transcription | A local speech model by default, a hosted one only if a key is present | audio of other people talking is the last thing that should leave the machine by default | | Summary | One model call over the transcript, every bullet carrying the line ids behind it | the published model card warns the transcript can contain text nobody said | | Library | SQLite index over a directory of meeting folders you can open in a file manager | the recordings should outlive this application | | Retention | Audio deleted on a schedule, transcript and notes kept | a recording of a colleague is a liability with a shelf life | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capture | Segmented recording and the consent note. | Any recorder, provided it writes closed segments rather than one file. | | Transcription | Chunking, silence removal, confidence signals. | Any model, provided timestamps and a no-speech signal survive. | | Summary | Citation-carrying bullets and verification. | Any provider, provided uncited bullets are dropped. | | Clips | Markers and cutting from the segment timeline. | Any cutter, provided a gap fails loudly. | | Retention | Audio expiry and real deletion. | Not swappable for keeping everything forever. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `MEETINGS_DIR` · required · One folder per meeting holding segments, transcript, notes and clips. - `DATABASE_PATH` · required · The index over the meeting folders. Rebuildable from them. - `SEGMENT_SECONDS` · required · Segment length. This is how much a crash can cost you. - `ASR_MODEL_PATH` · required · The local speech model. Its identifier is stored with every transcript line. - `SUMMARY_API_KEY` · optional, secret · Optional hosted model for summaries. Without it the transcript and clips still work. - `AUDIO_RETENTION_DAYS` · required · Audio is deleted after this. Transcripts, notes and clips survive. - `CLIP_LEAD_SECONDS` · required · Lead-in and lead-out either side of a marker when cutting a clip. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · tl;dv product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Record to short numbered segment files on disk, never one growing buffer, A local speech model by default, a hosted one only if a key is present, One model call over the transcript, every bullet carrying the line ids behind it, SQLite index over a directory of meeting folders you can open in a file manager, Audio deleted on a schedule, transcript and notes kept. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. - Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point.
# Delivery milestones · tl;dv Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Capture that survives a three hour call Recording writes continuously to disk in short segments and a mid-meeting failure costs seconds, not hours. ### Steps 1. Write numbered segments and close each one The meeting is the folder; the file is only the last few seconds at risk. Files: `src/capture/recorder.ts`, `tests/segment-crash.test.ts` 2. Find out what your platform will actually give you Capturing another application's audio is an operating system question with a different answer on each one, so check the current documentation for your version before designing around it. Files: `docs/audio-capture.md` 3. Ask for the consent note before the first byte Afterwards it is a form nobody fills in. Files: `src/capture/start.ts` ### Done when - [ ] A kill mid-recording loses at most one segment - [ ] System audio either works or is honestly reported missing - [ ] The consent note is required and stored - [ ] Recording refuses to start on a nearly full disk ### Watch out - Recording into one file that is only finalised on stop means a crash, a full disk or an unplugged interface leaves a file with no usable header and the whole meeting is gone, which is exactly when you most needed it. Write short segments, close each one properly, and treat the meeting as the folder rather than the file. ## M2 · A transcript the reader can check Every transcript line carries its timestamps and its confidence signals, and the audio behind it plays on a click. ### Steps 1. Transcribe in overlapping chunks and keep timestamps A boundary in the middle of a sentence is where transcripts go wrong. Files: `src/transcribe/chunks.ts`, `tests/overlap.test.ts` 2. Cut the silence before the model sees it Silence is where invented sentences come from. Files: `src/transcribe/vad.ts` 3. Store the confidence signals and show them The reader deciding whether to trust a line needs the number. Files: `src/transcribe/store.ts` ### Done when - [ ] Every line has timestamps and confidence signals - [ ] Clicking a line plays the matching audio - [ ] Silence produces no transcript lines - [ ] Overlapping chunks do not duplicate words ### Watch out - Feeding a long silence or a stretch of hold music to a speech model produces confident, grammatical sentences that nobody said, and they look identical to real transcript lines in a text file. Cut non-speech spans before transcription, keep the no-speech probability on every line, and mark low-confidence lines in the interface rather than hiding the number. ## M3 · A summary that cites or says nothing Notes and action items are generated only with the transcript lines they came from attached. ### Steps 1. Ask for line ids with every bullet A claim without a source is not a note, it is a guess. Files: `src/summary/prompt.ts` 2. Verify the citations before display The model will cite lines that do not exist. Files: `src/summary/verify.ts`, `tests/citations.test.ts` 3. Store model and prompt version with the summary So a strange bullet can be traced rather than argued about. Files: `src/summary/store.ts` ### Done when - [ ] Bullets with bad citations are dropped - [ ] Every bullet links to playable audio - [ ] No action items are invented from small talk - [ ] Model and prompt version are stored ### Watch out - A clean five bullet summary with no links is the most dangerous artefact this application can produce, because it is exactly the thing people forward, and neither the sender nor the receiver can tell which sentence came from a hallucinated transcript line. Require citations, verify them mechanically, and drop the bullets that fail rather than showing them unmarked. ## M4 · Markers while it happens, clips afterwards A hotkey during the meeting marks a moment, and each marker becomes a clip you can play or hand over. ### Steps 1. Register a hotkey that works without focus The moment worth clipping happens while you are looking at the call. Files: `src/capture/hotkey.ts` 2. Cut clips from the segment timeline Segment start and duration are the only reliable clock here. Files: `src/clips/cut.ts`, `tests/clip-boundary.test.ts` 3. Fail loudly across a gap A silent clip is a bug report you will never receive. Files: `src/clips/cut.ts` ### Done when - [ ] Markers land within tolerance without focus - [ ] Clips across segment boundaries play continuously - [ ] A clip across a gap fails with a clear message - [ ] Clips are ordinary files that play anywhere ### Watch out - Cutting a clip by seeking into a single concatenated file falls apart the moment a segment is missing or a device changed sample rate mid-meeting, and you get silence or a clip from the wrong minute. Build the clip from the segment timeline, using each segment's recorded start and duration, and fail loudly if the requested range crosses a gap. ## M5 · A library that forgets on purpose (production only) Meetings are searchable, the audio expires on a schedule, and deleting a meeting really removes it. ### Steps 1. Expire the audio, keep the notes The notes were the point; the recording was the means. Files: `src/retention/sweep.ts`, `tests/retention.test.ts` 2. Make deletion take the folder and the index Anything left behind makes your deletion claim untrue. Files: `src/meetings/delete.ts` 3. Export as plain files anyone can open This tool should not be needed to read your own meetings. Files: `src/export/folder.ts` ```sh npm run export -- --meeting=2026-09-15-standup ``` ### Done when - [ ] Audio expires on schedule and notes survive - [ ] Deleting a meeting leaves nothing on disk or in the index - [ ] Search finds a spoken phrase and links to its timestamp - [ ] An exported folder opens without this application ### Watch out - Keeping every recording forever because disk is cheap leaves you holding hours of other people talking, indexed and searchable, long after the reason for recording them ended, and the first time that matters is the worst possible time. Set a retention period on day one, delete the audio on schedule, and keep the notes that were the point.
# Operations · tl;dv ## Backup Back up the meeting folders, not the index, since the index rebuilds from them. Think carefully before backing up audio of other people to anywhere you do not control, and prefer backing up transcripts and notes only. ## Restore Restore the folders, reindex, and confirm the meeting count and that a random transcript line still plays its audio. If audio was deliberately excluded, the transcripts should restore alone without errors. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Free disk against the retention period, the proportion of transcript lines below your confidence threshold, and the number of summary bullets dropped for bad citations. A rising drop rate usually means the transcript quality fell, not that the summary model changed. ## Incident checklist The incident here is a person asking what you have of them and when it will be gone. The consent note, the retention setting and the recorded audio deletion times are the answer, so keep them accurate rather than aspirational. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A kill mid-recording is confirmed to lose at most one segment - [ ] Silence is confirmed to produce no transcript lines - [ ] Summary bullets with bad citations are confirmed dropped - [ ] Audio expiry is confirmed to leave transcripts and notes intact - [ ] Deleting a meeting is confirmed to leave nothing on disk or in the index ## Launch constraint Do not market omitted tl;dv capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. One folder per meeting holding segments, transcript, notes and clips. MEETINGS_DIR=~/Meetings # Required. The index over the meeting folders. Rebuildable from them. DATABASE_PATH=./data/meetings.db # Required. Segment length. This is how much a crash can cost you. SEGMENT_SECONDS=60 # Required. The local speech model. Its identifier is stored with every transcript line. ASR_MODEL_PATH=./models/speech-medium.bin # Optional · secret. Optional hosted model for summaries. Without it the transcript and clips still work. SUMMARY_API_KEY=sk-...redacted # Required. Audio is deleted after this. Transcripts, notes and clips survive. AUDIO_RETENTION_DAYS=30 # Required. Lead-in and lead-out either side of a marker when cutting a clip. CLIP_LEAD_SECONDS=30
$ choose a build depth, inspect the files, then open the complete pack in your agent
They pay for repeatable team-wide capture and distribution, not because summaries are hard.
xmeeting bot upkeep
xteam/shared library
xCRM integrations
xadmin controls
xpolished clip sharing
tl;dv pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free | $0/user | $0/user | Unlimited meeting recordings, transcripts, and storage; no retention clock. |
| pro | — | $18/user | Paid AI and workflow features above Free; exact current numeric feature caps were not exposed in crawlable public pricing text. |
| business | custom | — | Conversation intelligence, coaching, and revenue workflows; exact current numeric limits and public price were not reliably exposed. |
| enterprise | custom | — | Custom security, administration, and support; no public numeric price. |
free tierUnlimited recordings, transcripts, and storage; numeric caps for advanced AI features were not exposed in the crawlable live pricing application.
billingmonthly + annual; current monthly prices and Business annual price were not reliably exposed
hidden costsThe live pricing application is dynamic, and separate official 2026 tl;dv pages conflict on the Business annual figure; that field is intentionally null rather than choosing one.
verified 2026-08-14 · source ↗
Vibecode tl;dv
Kinda. The core of tl;dv is buildable in a weekend with the prompt on this page, but there are real gaps: meeting bot upkeep, team/shared library. Read the honest list above before committing.
How much does tl;dv cost?
tl;dv costs about $18/month (Pro, checked 2026-08-14), which is $216 per year.
What do I lose by replacing tl;dv?
Honestly: meeting bot upkeep; team/shared library; CRM integrations; admin controls; polished clip sharing. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to tl;dv?
Yes: whisper.cpp (Can power the transcription layer for a simpler local clone.). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.