Hold That Thought — Project Handbook (Flutter · Supabase · OpenAI)

App ID: com.holdthatthought
One-liner: Record thoughts (AAC .m4a), store locally + Supabase (DB + Storage), auto-transcribe (OpenAI Whisper), auto-summarize (Edge Function). Dark M3 “glass” UI. Offline-first with durable retries.


1) Architecture

  • Flutter/Android: audio record/play; dark M3; glass cards.
  • Storage:
    • Local app dir for audio.
    • Supabase Storage bucket thoughts-audio at path <uid>/<clientId>.m4a.
    • Supabase Postgres table thoughts.
  • Auth: Supabase anonymous auth; sessions persist + auto-refresh; all network paths re-ensure anon session.
  • Transcription: HTTP Whisper (Authorization: Bearer <OPENAI_API_KEY>); offline queue + retries.
  • Summaries: Edge Function index validates JWT; uses service-role client; adopts orphan rows; upsert by (user_id, client_id); returns summary. Local OpenAI fallback only if Edge unavailable.
  • Offline: durable queues (upload + transcription); auto-summary on upload complete; reconnect sweep to backfill.

2) Key Files

Bootstrap/Config

  • lib/bootstrap.dart — dotenv; Supabase.initialize (persistSession/autoRefresh); starts Sync + reconnect sweep + upload→summary listener.
  • lib/core/openai_key.dart — resolver + masking.
  • lib/core/auth_utils.dartensureSession() + ensureSignedInUid().
  • lib/services/edge_client.dart — Supabase Functions invoke.

Capture/Transcription/Summary/Sync

  • lib/pages/capture_page.dart — capture UI + enqueue upload.
  • lib/services/transcription_service.dart — Whisper.
  • lib/services/transcription_retry_queue.dart — backoff + pause/resume.
  • lib/services/upload_service.dart — queue, event stream, Storage upload.
  • lib/services/sync_service.dart — upload→summary hook + reconnect sweep + auth hardening.
  • lib/services/summary_service.dart — Edge-first; strict prompt (return only a 1–2 sentence summary; no chatter).
  • lib/widgets/thought_detail_bottom_sheet.dart — single Summary button + spinner; persistent summary card; transcript edit pencil in header.
  • lib/repositories/thought_repository.dart — smart status updates.

Settings/Diagnostics

  • lib/pages/settings_page.dart — glass cards; Fix Connection; key paste/validate (restart-safe).

Theme & Build

  • lib/theme/app_theme.dart — dark M3.
  • tool/pad_icon.dart, flutter_launcher_icons, flutter_native_splash — launcher/splash.
  • tooling/build_release.ps1 — one-click release pipeline.

Edge/Policies

  • supabase/functions/index.ts — CORS; JWT; service client; adopt orphan; upsert (user_id, client_id); returns {id, clientId, summary}.
  • supabase/policies/rls_thoughts.sql — RLS for thoughts.
  • Storage bucket thoughts-audio with per-user RLS: first path segment == auth.uid().

3) Shipped Highlights

  • Secrets via .env + secure storage; masked logs.
  • Anon auth with persisted session; all sync paths re-ensure session.
  • Recording to .m4a; upload to thoughts-audio/<uid>/<clientId>.m4a with retry.
  • Retry queues (401 → pause; resume on key fix); robust offline→online flows.
  • SummaryService Edge-first with clean upsert and strict “summary-only” prompt.
  • UI polish: single Summary button; spinner; persistent summary card; chips only when true (✨ Summarized when summary.trim().isNotEmpty).
  • Diagnostics & key validate; context-safe async; modernized APIs (SharePlus, PopScope, withValues(alpha)).
  • Release builds green; Impeller/Vulkan OK.

4) Current Status (Today)

✅ E2E on device: record → upload → auto-transcribeauto-summary.
✅ Reconnect sweep backfills; badges flow: Local → Uploaded → Transcribed → ✨ Summarized.
✅ Storage + RLS live; DB aligned: summary, transcript, client_id (NOT NULL, unique per user), updated_at trigger.


5) Next Up

Polish & Perf

  • Waveform seek bar; micro-animations for chips.
  • (Optional) Storage perf index: storage.objects (bucket_id, first_segment).

Edge telemetry

  • Structured logs/metrics for index.ts (latency, token usage, failures).

QA

  • Long offline sessions; large files; concurrent uploads.

6) Commands

Build & install

flutter clean
flutter pub get
dart run tool/pad_icon.dart 0.97
dart run flutter_launcher_icons
dart run flutter_native_splash:create
flutter build apk --release
adb devices -l
adb -s <ID> install -r build/app/outputs/flutter-apk/app-release.apk

Logs

adb logcat -c
adb logcat *:S flutter:V AndroidRuntime:E ActivityManager:E > app_startup.txt