Build Grain

KINDAback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local meeting archive whose hard problems are refusing to process a recording nobody consented to, transcribing long audio in chunks without losing or duplicating words at the seams, and producing notes where every claim cites a timestamp you can play.

estimated effort multi-daythe files for this build are in the project pack

RuntimePython 3.12 with FastAPI bound to localhost onlyTranscriptionwhisper.cpp run locally with segment and word timestampsMediaFFmpeg as a subprocess for probing, chunking and clippingStorageSQLite for meetings, transcripts and notes, media in a folder beside itNotesAn optional model behind one interface, with an extractive fallback

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the pipeline, the job runner and the local interface.

    Get it python.org, or your package manager. open ↗

    Verify python3 --version prints 3.12 or higher

  2. installfree

    Why Probing, chunking and clipping all go through it, and a build without ffprobe fails at the first import rather than at the first clip.

    Get it Your package manager, or a static build from the official downloads page. open ↗

    Verify ffprobe -version and ffmpeg -version both print a version

  3. installfree

    Why Transcription runs locally, and the model file is a separate download that is easy to forget until the first job fails.

    Get it Build whisper.cpp and download a model file of a size your machine can actually run. open ↗

    Verify The binary transcribes the bundled sample and prints timestamps

  4. have readyfree

    Why Chunking, resume and timestamp offsets are all fine on a five minute file and all break on a long one, which is the only length that matters.

    Get it A recording you made of a meeting where everyone was told, or a long public talk you are permitted to use.

    Verify It transcribes end to end and three random citations play the right words

  5. decidefree

    Why Both are enforced by code and asserted by tests, and writing them after the fact means writing whatever the code already does.

    Get it Write down who must be informed, how, and how long recordings and transcripts are kept.

    Verify Both appear in the README and their values are the ones the tests use

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, source_path, media_sha256, duration_seconds, imported_at, delete_after)
- consent(meeting_id, parties_json, informed_how, informed_at, recorded_by, note) · written before transcription is allowed to start
- segments(meeting_id, ordinal, start_ms, end_ms, speaker_label, text, confidence) · timestamps are absolute in the meeting, never per chunk
- words(segment_id, start_ms, end_ms, text) · what makes a clip cut land between words instead of through one
- notes(id, meeting_id, kind, text, source_segment_id, quote, created_at) · kind is decision, action, question or summary, and every row cites a segment
- clips(id, meeting_id, start_ms, end_ms, path, created_at)
- jobs(id, meeting_id, kind, state, chunk_index, attempts, last_error) · transcription is resumable, because a two hour file fails at minute ninety

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.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/meetings.dbMeetings, consent, transcripts and notes.
MEDIA_DIRrequired./data/mediaSource recordings, chunks and clips. Deletion must clear all three.
WHISPER_BINARYrequired/usr/local/bin/whisper-cliThe local transcription binary.
WHISPER_MODEL_PATHrequired./models/ggml-base.en.binThe model file. Bigger is slower and usually better; measure on your own machine.
CHUNK_SECONDSrequired600Chunk length for long audio. Timestamps are offset by the chunk start.
CHUNK_OVERLAP_SECONDSrequired5Overlap between chunks, de-duplicated on merge. Zero here means words lost at every seam.
RETENTION_DAYSrequired365Set at import. The scheduled job reports what it removed.
NOTES_MODELoptionalnoneOptional. Unset means the extractive fallback, which must stay usable.

The build, in order

  1. Intake that will not start without consent

    A recording cannot be transcribed until who was told is recorded.

    1. Duration, streams and codecs decide whether the rest of the pipeline can work at all.

      Files src/intake.pysrc/media/probe.py

    2. In the queueing code, so there is one place it can be bypassed and it is visible.

      Files src/jobs.pytests/test_consent_gate.py

    3. Decided once, when nobody is attached to the recording yet.

      Files src/retention.py

    done when · tick each as it passes
    watch out
    • Making consent a checkbox that is already ticked turns the whole record into a lie that looks like compliance, and a consent row written after transcription proves nothing at all. Require the parties and the method to be typed, block the transcription job until the row exists, and store the time it was written rather than the time the meeting happened.
  2. Long audio without losing the seams

    A two hour recording transcribes in chunks with one continuous clock.

    1. A few seconds of overlap is the cheapest fix for words cut in half.

      Files src/transcribe/chunk.py

    2. This one line is the difference between citations that work and citations that lie.

      Files src/transcribe/merge.pytests/test_timestamps.py

    3. Long jobs fail, and restarting a ninety minute transcription is how people give up.

      Files src/jobs.py

    done when · tick each as it passes
    watch out
    • Cutting a file into fixed ten minute chunks and concatenating the results slices words in half at every boundary and leaves each chunk's timestamps starting again at zero, so the transcript reads fine and every citation in it points at the wrong moment. Overlap the chunks, offset the timestamps by the chunk start, and de-duplicate across the overlap by matching text.
  3. Speakers labelled as honestly as you can label them

    Attribution is either evidenced or clearly provisional.

    1. A letter is honest; a guessed name is a claim.

      Files src/speakers.py

    2. Otherwise people rename the first three segments and give up.

      Files src/speakers.pytests/test_rename.py

    3. So the note can show that the attribution is weak.

      Files src/notes/render.py

    done when · tick each as it passes
    watch out
    • Guessing that the voice saying 'I will do it' belongs to the person whose name was said earlier produces an action item assigned to the wrong colleague, which is discovered in the following meeting. Keep labels anonymous until a person renames them, and carry the confidence into the notes so an attribution never looks more certain than it is.
  4. Notes that cite the second they came from

    Every decision, action and question quotes the transcript and links to a timestamp.

    1. The check is a substring match, and it catches almost everything invented.

      Files src/notes/verify.pytests/test_quote_verification.py

    2. A caveat next to a wrong action item does not stop anyone acting on it.

      Files src/notes/build.py

    3. Then the model is an improvement rather than a dependency.

      Files src/notes/extractive.py

    done when · tick each as it passes
    watch out
    • A model asked to summarise a meeting will produce a clean, plausible action item that nobody raised, and it will be the one item somebody acts on. Require a quote and a segment id on every row, check the quote actually appears in that segment before saving, and delete the rows that fail rather than keeping them with a caveat.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more meeting notes.