Build Bluedot

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 private meeting recorder whose hard problems are capturing audio that survives a crash, transcribing without letting the model invent sentences during silence, and writing notes that can be checked against the recording rather than trusted.

estimated effort weekendthe files for this build are in the project pack

RuntimePython 3.12 with a local web interface, nothing leaving the machineAudioffmpeg for conversion, fixed length chunks written to disk as you recordTranscriptionA local Whisper-family model, run after voice activity detectionStorageSQLite for segments and notes, audio as files beside itNotesGenerated only with segment ids, and dropped if the ids do not resolve

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 recorder, the transcription pipeline and the local interface.

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

    Verify python3 --version prints 3.12 or higher

  2. installfree

    Why Every audio conversion and the chunk stitching go through it, including the recovery path.

    Get it Your package manager, or ffmpeg.org. open ↗

    Verify ffmpeg -version prints a version and the stitcher runs on a fixture

  3. have readyfree, plus disk for the weights

    Why Transcription happens on your machine because the recording contains other people's voices, and the model size decides both accuracy and how long a meeting takes to process.

    Get it Download weights for a maintained local implementation and pick a size your machine can run in reasonable time.

    Verify A five minute fixture transcribes and the elapsed time is recorded

  4. installfree

    Why It is the component that stops silence reaching the model, which is where the fabricated sentences come from.

    Get it Any maintained VAD for your runtime, configured with the aggressiveness you tested.

    Verify A silent fixture yields no speech regions

  5. decidefree

    Why The technical part is one field; the part that matters is what you actually say at the start of a call, and deciding it now stops it being skipped later.

    Get it Write the sentence you will say and the sentence stored in the consent note, and put both in the README.

    Verify Both appear in the README and the recorder refuses to start without the note

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- recordings(id, started_at, ended_at, path, sample_rate, duration_s, consent_note, source_device, state)
- chunks(recording_id, ordinal, path, start_s, duration_s, sealed) · written during capture, stitched on stop
- segments(id, recording_id, start_s, end_s, text, avg_logprob, no_speech_prob, compression_ratio, model_id, flag_reason)
- speakers(id, recording_id, cluster_label, display_name) · the cluster is what the algorithm found, the name is what a human typed
- segment_speaker(segment_id, speaker_id, confidence) · attribution is stored with its confidence or not stored
- notes(id, recording_id, kind, text, created_at, model_id, prompt_version) · kind is summary, decision or action
- note_sources(note_id, segment_id) · a note with no rows here cannot be saved, which is the whole design
- There is no column holding a note without a segment behind it, and no column holding a speaker name as a fact.

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.dbRecordings, segments, speakers and notes. Survives the audio, so back it up.
AUDIO_DIRrequired./data/audioRecordings and chunk directories. Deleted on expiry, rows retained.
WHISPER_MODELrequireda local model identifierStored with every segment, because a different model gives different text.
AUDIO_RETENTION_DAYSrequired30After this, audio is deleted and the transcript stays. Shorter is a reasonable default.
FLAG_NO_SPEECH_PROBrequired0.6Above this, a segment renders marked and is excluded from note generation.
FLAG_COMPRESSION_RATIOrequired2.4Above this, the segment is treated as runaway repetition rather than speech.
CHUNK_SECONDSrequired30How much audio a crash can cost. Lower means more files and less loss.

The build, in order

  1. Capture that survives the crash

    Recording writes to disk continuously, shows its state, and cannot start without a consent note.

    1. The only defence against losing an hour is not holding an hour.

      Files src/capture/recorder.pytests/test_crash_recovery.py

    2. Friction here is the feature, not a bug to be smoothed off.

      Files src/capture/consent.py

    3. Recovery has to work with no process alive to help it.

      Files src/capture/stitch.py

    done when · tick each as it passes
    watch out
    • Buffering the whole meeting in memory and writing one file at the end means a crash, a full disk or a closed laptop at minute 58 loses the hour, and this is the failure people discover exactly once. Write sealed chunks as you go, keep a manifest beside them, and make the stitcher able to run against a directory with no process attached to it.
  2. Transcription that admits what it is unsure about

    Speech is detected before it is transcribed, and every segment keeps the scores that say how confident it was.

    1. The model should never be asked what silence said.

      Files src/transcribe/vad.pytests/test_silence.py

    2. You cannot mark a doubtful segment you did not measure.

      Files src/transcribe/segments.py

    3. A loop is the model's tell and it reads as normal text.

      Files src/transcribe/flags.py

    done when · tick each as it passes
    watch out
    • Feeding a whole meeting file straight into the model, silences included, is where the fabricated sentences come from: the published audit found roughly one percent of transcriptions containing phrases present nowhere in the audio, concentrated in recordings with long non-vocal stretches, which describes every meeting recording ever made. Detect speech first, never send a region that is all silence, and keep the scores so a doubtful segment can be shown as doubtful.
    • A repeated phrase looping for thirty seconds is the classic signature of a model stuck on low-information audio, and it reads as a real transcript at a glance. Check the compression ratio of each segment's text against a threshold and flag runaway repetition instead of storing it as speech.
  3. Speakers are a guess with a name attached

    Speaker attribution is presented as a cluster somebody labelled, never as a fact the system knows.

    1. The algorithm found a cluster; a person supplied a name.

      Files src/speakers/cluster.py

    2. A wrong attribution in notes is worse than no attribution.

      Files src/speakers/attribute.pytests/test_attribution.py

    3. You will re-run diarisation and you will want the names back.

      Files src/speakers/rename.py

    done when · tick each as it passes
    watch out
    • Rendering a diarised label as a plain name means a decision gets attributed to the wrong person in the notes, and nobody catches it because a transcript with names in it looks authoritative. Keep the cluster id separate from the display name, show the attribution confidence, and make a segment with low confidence obvious in both the transcript and anything generated from it.
  4. Notes that point back at the recording

    Every summary line, decision and action item carries the segment ids it came from, or it is not saved.

    1. A citation you did not ask for will not arrive.

      Files src/notes/generate.pysrc/notes/prompt.py

    2. An invented id is the model telling you it guessed.

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

    3. Code paths get added later; constraints do not.

      Files src/db/schema.sql

    done when · tick each as it passes
    watch out
    • Asking for a summary of the whole transcript and printing what comes back gives you action items that sound exactly right and belong to nobody, and the person who missed the meeting will act on one. Number the segments in the prompt, require ids per note, verify each id exists, and treat an unresolvable id as a dropped note rather than a note with a missing link.
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.