TaleWater โ€” how your fishing audio becomes data

Two views: how it runs today, and where it's headed (fully backed by your servers). The colors tell you where each thing lives.

On your phone Outside AI / map services (they keep nothing) Claude (the LLM that does extraction) Your backend / cloud database

Today โ€” your phone is the brain

As built right now

Almost everything happens on the phone. The phone holds the real database, and it reaches out directly to outside AI services to do the heavy lifting. You have almost no backend yet.

๐Ÿ“ฑ On your phone (the app)

The Expo / React-Native app. This is where you record, and it's where your data actually lives.

๐ŸŽ™๏ธ Microphone captureRecords a session to a small audio file in temp storage (16kHz mono).
๐Ÿ“ GPS loggerDrops a location pin every ~10s while recording. Runs in the background.
๐Ÿ“„ Plaud import (alt. path)You can instead pick 2 text files exported from a Plaud recorder. Parsed on the phone.
๐Ÿ—„๏ธ SQLite database โ€” the source of truthThe real database lives on the phone: transcripts, every extracted catch/encounter/technique, your GPS track, journal entries, fisheries. Works fully offline.Authoritative store
๐Ÿ”‘ Both API keys are baked into the appThe Whisper key and the Claude key currently ship inside the installed build. Known-temporary; the fix is moving them to a server (see Future).Security debt

โ˜๏ธ Outside services

The phone calls these directly. They process and reply โ€” they don't store your data.

๐ŸŽง Whisper (on Replicate)Receives your audio file, returns the transcript text. The audio is deleted from your phone right after the transcript comes back.Sees: audio
๐Ÿง  Claude โ€” Anthropic APIReceives only the transcript text. Returns structured JSON: catches, encounters, techniques, water conditions, and journal "knowledge". A 2nd Claude call clusters those into journal entries.Sees: transcript text only
๐ŸŒฆ๏ธ Open-Meteo ยท ๐Ÿ—บ๏ธ Maps / geocodingWeather backfill (gets coords + time), map tiles, place lookup. Minor helpers.

๐Ÿ—„๏ธ Your backend (tiny today)

Supabase โ€” and it does almost nothing yet.

๐Ÿ” Login / identitySupabase Auth handles accounts. That's it for "real" backend use today.
๐Ÿ“ฅ Extraction review copyA write-only pilot_extraction table gets a copy of each extraction + transcript, so you can review extraction quality. Not your main data store.Review only
โŒ No shared database yetNo per-user data sync, no audio storage bucket. Your real data does not live here today.

The path, start to finish (today)

  1. phoneYou talk. The app records audio and logs GPS pins in the background. (Or you import 2 Plaud text files instead of recording.)
  2. phonewhisperAudio โ†’ transcript. The phone uploads the audio file to Whisper, gets text back, then deletes the audio from the phone.
  3. phoneclaudeTranscript โ†’ structured data. The phone sends just the transcript text to Claude. Claude returns catches, techniques, conditions, and journal knowledge as JSON.
  4. phonePin the events on the map. The phone matches each extracted event to the nearest GPS pin by timestamp โ€” Claude never sees your location.
  5. phoneSave. Everything is written to the phone's SQLite database โ€” the real, authoritative copy.
  6. backendQuality copy. A copy of the extraction is fired off to Supabase so you can review how good the extraction was. (Fire-and-forget; nothing depends on it.)
๐ŸŽ™๏ธ Deep dive โ€” inside the recording pipeline the 8 states a recording moves through, and where capture actually breaksโ–ถ

This is one level below the โ€œMicrophone captureโ€ box above โ€” the part youโ€™re debugging. Every recording is a row in the phoneโ€™s SQLite recordings table that walks a fixed ladder of statuses. The audio bugs all live at a specific rung.

the status machine ยท src/db/recordings.ts
recordingโ†’ capturedโ†’ uploadingโ†’ transcribingโ†’ transcribedโ†’ extractingโ†’ complete โœ“ ยทfailed โœ•

The first six are NON_TERMINAL โ€” anything not complete or failed is treated as unfinished, and is a candidate for automatic recovery. That recovery machinery is what makes a recording survive an app kill โ€” and itโ€™s also where the subtlest bug lives.

on the phone โ€” the capture lifecycle

Format. Capture is 16 kHz mono AAC (.m4a) via expo-audio (src/lib/audio-recorder.ts). The live mic level (dBFS) is read from getStatus().metering and drives the Record Consoleโ€™s visualizer.

Interruptions pause, they donโ€™t stop. A phone call, Siri, a Bluetooth route change, or another app grabbing the mic pauses the recorder mid-session. src/hooks/use-session-recorder.ts detects this, flags the session as having a gap, keeps the visualizer honest (it must not look like itโ€™s recording while paused), and auto-resumes once the interruption clears.

Stop always finalizes โ€” even when paused. A paused recorder still holds a half-written .m4a with no moov atom โ€” an unplayable file. expo-audioโ€™s stop() accepts the paused state and writes the moov, producing a playable file. Finalizing is therefore not gated on โ€œis it currently recordingโ€; skipping it on the paused case is what used to strand audio and hang transcription downstream.

off the phone โ€” the resumable stages ยท src/lib/recording-pipeline.ts

runNativeRecordingPipeline runs the work as resumable stages, each keeping its input until the result is durably saved โ€” so a kill or a flaky network never dead-ends a recording; the next run resumes from the last completed stage. The chain: VAD trims silence to speech segments โ†’ those upload to WhisperX on Replicate โ†’ the transcript returns and the audio is deleted โ†’ the transcript goes to Claude for extraction โ†’ map-placement โ†’ dedup โ†’ journal clustering โ†’ observations are written and the row goes complete. Per-stage progress + timing is persisted (S-D-46) so the Log card shows a live stage and a running timer.

the recovery loop โ€” and the trap

Every time the app foregrounds (AppState โ†’ 'active'), reconcileAudioRecordings scans for unfinished (NON_TERMINAL) rows and calls decideResumeAction to resume or fail each. Itโ€™s fenced by an in-memory in-flight set and a durable attempts cap so it canโ€™t loop forever.

The live-capture trap.'recording' is itself in NON_TERMINAL โ€” so a recording you are currently making also looks like a โ€œrecovery candidate.โ€ The in-flight guard is only populated when you press Stop; lock the phone and unlock it mid-recording and the foreground reconcile can run first, see a live recording row that looks orphaned, and fail it. Itโ€™s a race against your own Stop. DEF-125
where audio breaks โ€” by stage
During recording โ€” interruptions.Pause not resumed, finalize skipped on a paused recorder (no-moov file), or the visualizer falsely showing โ€œrecordingโ€ while paused. DEF-119 ยท DEF-120 ยท DEF-121
During recording โ€” reaped by recovery.The live-capture trap above: the foreground reconcile fails an in-progress capture. DEF-125
During transcribing โ€” a silent hang.A transcription network call that never settles strands the row in transcribing with no in-session retry; only a cold app launch rescues it. DEF-118
During extracting โ€” a stalled journal pass.The journal-clustering call cancels or idle-times-out (the network-stall family) โ†’ no Knowledge / Species / Skills written. DEF-110 ยท DEF-115

Live status of each defect lives in the bug register (Product โ†’ bugs) โ€” deliberately not pinned here, so this page doesnโ€™t drift stale.

Future โ€” your backend is the brain

Where it's headed

The heavy lifting and the source-of-truth move off the phone and onto your servers. The phone becomes a thin client. Keys leave the phone. Data becomes multi-user and centrally stored.

๐Ÿ“ฑ On your phone (thin client)

Captures and displays. No longer the brain.

๐ŸŽ™๏ธ Capture + ๐Ÿ“ GPSSame recording and location logging.
๐Ÿ“ฒ Local cache copyKeeps a synced copy so the app still works offline โ€” but it mirrors the cloud, it's no longer the master.Mirror, not master
โœ… No API keys on the deviceThe phone only talks to your backend. No Whisper/Claude keys to leak.Keys removed

โ˜๏ธ Outside services

Same services โ€” but now reached through your backend, never directly from the phone.

๐ŸŽง WhisperCalled by your server. Audio never routes through the phone's own keys.
๐Ÿง  Claude โ€” Anthropic APICalled by your extraction proxy. Same job (transcript โ†’ structured data), but the key and the spend limits live on your server.
๐ŸŒฆ๏ธ Weather ยท ๐Ÿ—บ๏ธ MapsSame helpers.

๐Ÿ—„๏ธ Your backend (the new center)

This is where the work and the data now live.

๐Ÿ›ก๏ธ Extraction + transcription proxyHolds the Whisper & Claude keys, caps daily spend, stores nothing. The phone calls this; it calls the AI services.Keys + spend control
๐Ÿ—„๏ธ Cloud database (Postgres) โ€” the source of truthEvery row keyed to a user, protected per-account. The master copy of all data. The phone syncs to it.Authoritative store
๐Ÿชฃ Storage bucketsFor audio / photos if you choose to retain them.
๐Ÿ” Auth + ๐Ÿง‘โ€๐Ÿ’ผ operator back-officeMulti-user accounts; your back-office reads from this same database. Enables sharing between anglers.

The path, start to finish (future)

  1. phoneYou talk. The phone captures audio + GPS, then hands it to your backend.
  2. backendwhisperclaudeYour server orchestrates. The proxy runs Whisper, then Claude โ€” keys safe server-side, spend capped, transcripts not logged.
  3. backendStore, keyed to you. Structured data lands in the cloud Postgres under your user account.
  4. backendphoneSync back. Your phone pulls a local copy so it still works offline.
  5. backendShare + operate. The operator back-office and (later) other anglers read from the same cloud database.

Straight answers to your questions

What's on my phone vs. elsewhere?

Today: nearly everything is on the phone โ€” the app, the recording, the GPS log, and the entire real database. The cloud only holds your login + a quality-review copy. Future: the phone becomes a thin client; the master database moves to your servers.

What data does Claude have access to?

Only the transcript text of what was said. Not your GPS/location, not the audio, not who you are, not any past session. Each extraction is a fresh, anonymous text-in / JSON-out call. Your location gets attached afterward, on the phone.

Where does my audio go?

To Whisper (on Replicate) to be turned into text, then it's deleted from the phone. It is not stored anywhere long-term today. (Future: optionally retained in your own storage bucket.)

Who has my API keys?

Today: both keys are baked into the installed app โ€” extractable, a known security debt. Future: keys live only on your backend; the phone never holds them.

Where is my fishing data stored?

Today: on the phone (SQLite). Single device, single user โ€” no cloud copy of the real data. Future: a multi-user cloud database, one secured slice per account, mirrored to each phone.

What triggers extraction?

Today: you do โ€” by stopping a recording or importing files. Nothing is automatic or scheduled. Future: same trigger, but the work runs on your backend instead of the phone.