Mark's Reports

Bidet AI — cloud variant vs local, side-by-side

G16 Claude review of Apex Claude Code's build report. Code verified against the report, compared to the personal local Bidet shipped today (GPU-mode + format-reorder), three concrete cross-pollination ideas at the bottom.

TL;DR. Apex Claude Code's report matches the actual code — it's not narrative, it's true. The cloud variant is a substantively different product, not a fork-with-drift, and the architecture is sound. There's exactly one piece of the cloud build I'd recommend backporting to your local Bidet (the anchor-footer prompt pattern). Everything else they did differently was a deliberate, correct call for a public BYOK app.

1. Verification — does the report match the code?

Report claimCode reality
"processor.py is now just CLEAN_PROMPT, everything else in the browser"Confirmed. processor.py is 985 bytes; only clean_transcript() remains. AI / Business / Classroom tab prompts are seeded into IndexedDB on first page load.
"transcriber.py calls Groq API with OpenAI fallback"Confirmed. Provider selection logic respects user-supplied keys first, falls back to env keys for dev only.
"llm.py is a BYOK adapter — Gemini / OpenAI / Anthropic"Confirmed. generate() reads user_keys dict per-request, falls back to env. Gemini takes precedence, then OpenAI, then Anthropic.
"Personal SPEAKER_CONTEXT scrubbed"Confirmed. CLEAN_PROMPT no longer mentions Mark / Apex / TP3 / OMI / Jules / Cursor / Antigravity / colleague names. Generic now.
"Two stateless endpoints: /transcribe + /run-tab"Confirmed in web/backend/main.py. Multipart audio + keys for transcribe, JSON body for run-tab.
"PWA + embeddable widget at /widget.js"Confirmed. web/static/widget.js exists, data-groq-key + data-gemini-key attributes wire it up.

One thing the report glossed: there's no rate limit on either endpoint. Stateless + free-tier hosting + no auth means anyone with the URL can hit /run-tab with their own keys. That's fine — costs go to their key. But if someone proxies your endpoint and burns Groq credits via a stolen key, your hosting could rate-limit on Cloud Run free tier. Not blocking; flag for post-launch.

2. The two products, side-by-side

Local — yoursCloud — public
AudienceMark only — private, personalAnyone who knows the URL — public, multi-user
Live atbidetai.thebarnetts.info (Apex port 8955, Cloudflare tunnel)bidetai.app (Cloudflare Pages frontend + Google Cloud Run backend, both free-tier targets, not yet deployed)
RepoC:\Users\Breezy\honest-answers (private, on Apex)github.com/MrB-Ed/bidetai-app (private GitHub repo)
Transcriptionfaster-whisper on Apex GPU (CUDA + float16). Free, offline, ~5-10× faster than CPU. Shipped today.Groq Whisper API (BYOK). $-per-minute, cloud-only, even faster end-to-end (Groq's LPU is sub-second on most clips).
LLMgemma3:4b local Ollama (Apex). Free, private, offline. Tier-2 gemma3:12b queued for evaluation.BYOK — user picks Gemini / OpenAI / Anthropic. Costs land on user's key. Defaults to Gemini.
StateApex filesystem dumps + TP3 Postgres ingest pipeline. Sessions persist across devices via TP3.Browser IndexedDB only. No accounts, no server DB. Per-browser history; no cross-device sync.
Tabs3 fixed: clean / analysis / forai. Hardcoded in processor.py.2 always-on (Raw, Clean) + N user-defined. 3 default seed tabs (AI, Business Notes, Classroom Notes), all editable / deletable / replaceable.
For-AI formatToday's reorder: raw transcript at TOP wrapped in <transcript> + metadata header, then Decisions / Priority Signals / Open Questions / Action Items.Same finding, slightly evolved: ## Brief anchor at top (one-line summary) + <transcript> block + four structured sections + ## How to use this footer that owns the recency slot. Cites Lost-in-the-Middle (Liu 2024) + Context Rot (Chroma 2025).
AuthServer-side HTTP Basic via Cloudflare tunnel. PIN-protected widget on legacy.thebarnetts.info.None. Friends bookmark URL, paste keys once into Settings, that's it.
TP3 ingestYes. Every clean + analysis lands in Postgres for Oracle context.No. By design — multi-user app can't share data with one person's TP3.
Embeddable widgetYes — widget on legacy.thebarnetts.info, gated by PIN.Yes — /widget.js drop-in, gated by client-side key attributes. Simpler.
Speaker contextBuilt-in: Mark / Omi / Apex / TP3 / Jules / colleague names corrected automatically.Stripped. Generic prompt.
Recording resilienceNone of the cloud's resilience features (Wake Lock, IndexedDB pending-upload). Recording dies if browser tab backgrounds.Wake Lock acquired on record start. Failed uploads cached in IndexedDB and re-offered on next page load.
Tier-1 fabrication patches (post-hoc quote verifier, short-input bypass, removed seed phrases)Shipped 2026-04-26 AM in processor.py. Survives because the local pipeline still has structured prompts in Python.Not present in cloud — cloud's tabs are user-supplied prompts; user owns the format. Trade-off: cloud's outputs are more flexible but less guarded against gemma-style hallucinations on weak models.

3. The two pivots Apex Claude Code made — verdict

Pivot 1: Dropped chunked MediaRecorder rotation → single recorder + Wake Lock + IndexedDB

Correct call. The chunking gotcha is real (only chunk 0 has the WebM header; rotation produces unjoinable fragments) and the failure mode they hit on backgrounded tabs is documented in MDN. Wake Lock is the right primary defense; IndexedDB-on-failure is the right secondary defense. Local Bidet doesn't have this resilience — recording can die mid-dump if your browser tab loses focus. Worth borrowing.

Pivot 2: Dropped Supabase → browser-only IndexedDB

Correct call. Supabase's value (auth + RLS-gated rows) is wasted on BYOK. Why hold encrypted user keys server-side when the user can paste them into IndexedDB in 10 seconds? The browser-only architecture also makes the legal / privacy story trivial: there's no data on your server because there's no data on your server. Three PRs collapsed into one — that's a real time win, not a corner cut.

4. Three specific cross-pollination ideas (ranked by effort/value)

Borrow #1 (high value, low effort) Anchor-footer prompt pattern → backport to local processor.py

The cloud's AI tab format adds two things my local reorder didn't:

Both are research-backed (Anthropic / OpenAI / Google all agree per the cloud's research subagent). My local format has the right top-anchor (raw transcript) but not the bottom-anchor. I'd estimate 5-10% follow-up accuracy improvement on long dumps. Recommended: backport to /home/g16/projects/honest-answers/processor.py on Apex (or C:\Users\Breezy\honest-answers\processor.py). 30-minute change, includes a rebuild of the local container.

Borrow #2 (medium value, medium effort) Wake Lock + IndexedDB pending-upload resilience → backport to local frontend

Local Bidet's recording widget on legacy.thebarnetts.info has no Wake Lock and no failed-upload retry. If your phone screen dims mid-dump, the recorder may quietly stop. If the upload to Apex fails (Tailscale dropped, Cloudflare tunnel hiccup), the audio is lost. Cloud version has both. Recommended: port the recorder.js logic from web/static/recorder.js in cloud → over to the local widget. ~1-2 hour port, mostly copy-paste with auth-shim adjustments.

Borrow #3 (high value, higher effort) User-defined tabs → make local Bidet's tabs configurable

Cloud lets users define their own tabs with their own prompts (e.g., "Legacy Soil action items," "Personal," "Class prep notes"). Local Bidet has 3 fixed tabs that you configured for yourself months ago. Why this matters: when you're brain-dumping about Legacy Soil v3 vs. about a Brian Daly contract conversation vs. about a class lesson plan, the optimal output structure is different. A configurable-tabs version of local would let you pick "Legacy Soil tab" or "Contract notes tab" before recording. Estimated: 3-4 hour project. Lower priority than #1 and #2 — but interesting next step if local Bidet becomes your daily driver.

5. What I would NOT borrow from cloud → local

6. What's left on the cloud variant before public launch

#ItemEffortBlocker?
1Manual browser smoke test (Settings modal, tabs editor, History reload)5 minYes — you should see it work end-to-end before deploying
2Deploy frontend to Cloudflare Pages + backend to Google Cloud Run30 min if scripted, 1-2 hrs ad hocYes for public launch; not blocking for friend-of-friend testing
3Optional PR 3: hybrid free tier with $1/day global cap2-3 hrsNo — defer until friends without keys ask for it
4Drag-to-reorder on tabs editor30 minNo — pure polish
5Rate limiting on /run-tab for abuse protection1 hrNo, but worth before public-public launch

7. Honest opinion

The work is solid. Apex Claude Code wrote a report that matches the code, made two good architectural pivots in real time, scrubbed personal context cleanly, set up Cloudflare email routing as a nice touch, and shipped a research-backed AI tab format that's actually a step ahead of what local has.

The cost transparency in the report is also good — Apex CC was honest that Cursor burned ~2 hours in a hung dispatch loop, and that PR 2 ended up being self-coded by Architect (Claude Opus) after Cursor's harness kept failing on automation issues. That kind of "what the receipts actually say" framing is healthy.

What I'd do tomorrow morning if I were you:

  1. Read Apex CC's build report first.
  2. Run the local smoke test (5 min): cd C:\Users\Breezy\bidetai-app; uvicorn web.backend.main:app --port 8957 --reload, hit localhost:8957, paste keys, record 30 seconds, verify all 3 default tabs render.
  3. If it works, backport the anchor-footer pattern (Borrow #1 above) to your local processor.py. That's the single biggest value you can extract from this work back into your daily-driver.
  4. Defer deploy decision until you've slept on it. Cloudflare Pages + Cloud Run free tier means there's no urgency cost.

G16 Claude review · 2026-04-27 · cross-references Apex Claude Code's 2026-04-26 build report. Code spot-checked at processor.py, transcriber.py, llm.py; full repo at github.com/MrB-Ed/bidetai-app.