Build Jamie
KINDAback to the verdict
A local transcription and notes pipeline whose hard problems are consent, getting long audio through a model without losing an hour's work to a crash, and producing notes where every claim can be traced back to the words that produced it.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the pipeline and the local web interface.
Get it python.org, or your package manager. open ↗
Verify
python3 --version prints 3.12 or higher - installfree
Why Every input is normalised to the format the transcriber documents, and this is the tool that does it.
Get it Your package manager, or the official builds. open ↗
Verify
ffmpeg -version prints a version and the conversion command runs - installfree, after the model download
Why Local transcription is the whole reason this is private, and the model file is a separate download from the binary.
Get it Build from the repository and fetch a model with the provided script. open ↗
Verify
The command line transcribes a short 16 kHz mono WAV - decidefree
Why Whether you may record with one party's consent or need everyone's changes what this app is allowed to do, and it is a question with an actual answer.
Get it Read your jurisdiction's wiretapping or interception statute, or its regulator's guidance, and write a paragraph.
Verify
The paragraph is in the README and the import gate reflects it - have readyfree
Why Resume, chunk stitching and retention are only testable on something over an hour, and a two minute clip proves nothing.
Get it Record yourself reading for ninety minutes, or use a public-domain recording with a known transcript.
Verify
It exceeds an hour and you know what it says
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- meetings(id, title, occurred_at, participants_json, consent_recorded_by, consent_recorded_at, source_filename, duration_s) - media(meeting_id, original_path, original_sha256, normalised_path, sample_rate, channels, codec, converted_at) - chunks(meeting_id, ordinal, start_s, end_s, overlap_s, state, attempts, transcript_path, finished_at) · state is pending, running, done or failed - segments(meeting_id, id, start_s, end_s, speaker_label, text) · speaker_label is anonymous until a human maps it - speakers(meeting_id, label, mapped_name, mapped_by, mapped_at) · empty until somebody says who is who - notes(meeting_id, kind, text, start_s, end_s, quote, confidence) · kind is decision, action or context, and a row without a quote is never written - retention(meeting_id, audio_delete_after, deleted_at) · the audio goes, the notes stay - Every notes row points at a span of segments, which is what makes the output checkable rather than plausible.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATA_DIR | required | ./data | Database, audio, normalised copies and chunk transcripts. All of it sensitive. |
WHISPER_MODEL_PATH | required | ./models/ggml-base.en.bin | The downloaded model file. Larger is slower and more accurate. |
CHUNK_SECONDS | required | 600 | Chunk length. Smaller means less lost to a crash and more seams to stitch. |
CHUNK_OVERLAP_SECONDS | required | 5 | Overlap used to remove duplicated words at boundaries. |
AUDIO_RETENTION_DAYS | required | 30 | After this the recording is deleted and the deletion is logged. Notes survive. |
NOTES_API_KEYsecret | optional | sk-...redacted | Only if you use a hosted model for extraction. Unset means local extraction and no audio or transcript leaves the machine. |
The build, in order
Consent, then audio in the one format that works
A file is accepted only with a consent record, and is converted to exactly what the transcriber wants.
The import path refuses without it, and the test proves the refusal.
Files
src/import.pytests/test_consent_gate.pySample rate, channels and codec, then probe the result.
Files
src/audio.pytests/test_normalise.pySo a bad transcript can be traced to a bad input.
Files
src/media.py
done when · tick each as it passeswatch out- Handing a 48 kHz stereo recording from a phone straight to the transcriber produces either an outright error or, worse, a transcript that is subtly wrong throughout with no indication why, and you spend an evening blaming the model. Normalise every input to the documented format first, store the original untouched, and log the conversion parameters against the meeting.
Long audio that survives a crash
A two hour recording transcribes in resumable pieces, and a failure costs one piece.
The difference between a resumable job and a gamble.
Files
src/transcribe.pytests/test_resume.pyA sentence cut in half is a sentence transcribed twice.
Files
src/stitch.pyNinety-five percent of a transcript is useful; nothing is not.
Files
src/transcribe.py
done when · tick each as it passeswatch out- Transcribing a two hour file in a single call means a crash, an out-of-memory kill or a closed laptop at the ninety minute mark throws away ninety minutes of compute, and the obvious retry throws away another ninety. Chunk it, persist each chunk's result before moving on, and make restart resume rather than start.
Speakers, without guessing who anyone is
Turns are separated and labelled anonymously until a person says who each label is.
Names enter only at render, from the human mapping.
Files
src/speakers.pytests/test_no_name_inference.pyFirst lines per label beats a waveform nobody can read.
Files
src/templates/speakers.htmlSo fixing a mistaken mapping is one update, not a migration.
Files
src/render.py
done when · tick each as it passeswatch out- Letting the summariser work out who is speaking from context puts a name against a commitment nobody made, and because the sentence reads naturally, the wrong person is chased for the wrong task a week later. Keep labels anonymous through the pipeline and substitute names only from the human mapping, at render time.
Notes that point at the words
Every decision and action item carries the timestamped quote it came from.
And verify it against the transcript before writing the row.
Files
src/notes.pytests/test_quote_grounding.pyLong-context summarisation is where invented commitments come from.
Files
src/notes.pyA note you can hear is a note you can settle an argument with.
Files
src/templates/meeting.html
done when · tick each as it passeswatch out- Feeding a thirty thousand word transcript in one request and asking for a summary produces smooth, confident output containing action items nobody assigned, and there is no way to tell which ones by reading it. Extract per chunk with a required verbatim quote, verify the quote exists in the transcript, and discard the item if it does not.
Markdown out, audio goneproduct builder
Notes leave in a plain format and the sensitive part does not linger.
Consent record included, because it is part of the record.
Files
src/export.pyRetention that nobody runs is a promise, not a policy.
Files
src/retention.pytests/test_retention.pyChunk transcripts and normalised copies are recordings too.
Files
src/wipe.pytests/test_wipe.py
done when · tick each as it passeswatch out- Keeping every recording forever because storage is cheap means a laptop holding two years of confidential conversations from people who agreed to one meeting being recorded, and the first time that matters is the worst possible time. Set a retention, delete on schedule, log each deletion, and make the notes stand alone without the audio.
That is the whole plan for Jamie. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- A meeting bot that joins calls.
- Live real-time transcription.
- Calendar and CRM integrations.
- Cross-meeting analytics on people.
- calendar and CRM integrations
- cross-call team analytics
- recording recovery, mastering, hosting, and distribution
- meeting-bot auto-join
- live multi-speaker accuracy
- Search across meetings, which is easy once segments carry timestamps and speaker labels
- A weekly digest of open action items, built from the notes table and nothing new
- Importing a transcript from elsewhere, skipping phases one and two entirely
Need the files? The project pack on the verdict page hands your agent the whole brief · more meeting notes.