Overview
A single-operator operating journal I designed and ship as the system I actually run with: messy team communication as the source of truth, AI that proposes writes, and a human who still has to approve them. Unstructured dumps in — WhatsApp exports, standups, screenshots, developer reports — dated evidence out.
Challenge
As CTO I am the translation layer between what the team said and what the company later treats as true. That work has three frictions generic tools do not model.
First, executive context switch: the same hour can hold a client complaint, a blocked deploy, a people question, and a decision I will be asked about years later. Ticket boards want a ticket; note apps want a page; neither wants the dump already in the clipboard.
Second, fragmented channels: status lives in chat threads, git, and long technical reports — not in the tool that will later be treated as the record. Meeting notetakers miss the channel where the work actually moved.
Third, tracking friction: if logging the day costs more than about ninety seconds, the day does not get logged. If retrieval is “search a folder,” last year’s decision is gone. If a model writes behind a service account, there is no honest undo.
I needed external memory that accepts reality in the form it arrives, keeps facts separable from interpretations, and answers against dated evidence — not a vibe.
Approach
Invert the usual order. Do not demand structure at ingest. Extract it, propose it, and wait for approval.
Raw text, screenshots, and WhatsApp archives parse in the browser first. Claude proposes typed writes — day notes, entries, tasks, person-log rows. The browser shows approval cards. Nothing reaches Firestore until I confirm. Chat then asks questions against a resolved scope (day, person, client, or an approved monthly rollup) and lists which documents it used.
One operator. Google sign-in. The model never holds the keys.
Solution
Client: React 19, TypeScript, Vite, Tailwind — a PWA so the same loop works on a phone. Manual UI is the primary surface; AI is an intermediary.
Data: Firebase Auth (Google only) and Firestore under workspace-scoped paths so personal and company journals do not collide. Storage for media, WhatsApp originals, and exports. Date-prefixed ids so chronological order is also console order.
AI plane: a Cloudflare Worker is the only process that sees the Anthropic API key. It verifies the Firebase ID token, injects AI rules and taxonomy, and returns tool calls. It never writes Firestore. The signed-in owner commits batches in the browser under security rules.
Ingest: Capture accepts paste and clipboard images (Storage + OCR). WhatsApp import parses iOS and Android exports entirely client-side — multi-line messages, media placeholders, locale quirks — denoises cheaply, archives months verbatim, then optionally runs digests with a pause/resume checkpoint. GitHub tracking is an engineering-frequency feed (who pushed, which branch), with merge noise separated rather than deleted; the PAT never leaves the Worker.
Retrieval: cheapest sufficient tier first — a named day or week, an approved monthly rollup instead of a raw month, a person hub, a client hub. No vector store. Vague questions load nothing until tools hunt. Present-tense notes are treated as snapshots of that calendar day, not as still-true status.
Anti-hallucination is stacked, not hoped: grounded ask-mode, temporal snapshot rules in the Worker prompt, human-in-the-loop writes, completion months recorded as fields, rollups that stay drafts until approval, append-only audit with undo, and a context list under every answer so a wrong reply is usually a retrieval bug you can rephrase.
Results
Used as the daily operating log — the product solves its own problem. Capture collapses a day that used to mean retyping into tickets into a short propose-and-approve loop. “What happened in July?”, “what is X on?”, and “what shipped last month?” become scoped reads over dated documents, person hubs, completed-task months, and GitHub frequency — not reconstruction from chat history. Bad AI writes are visible, gated, and undoable. Full JSON export so the record is not a hostage.
Reflection
The hard part was never “add a chatbot to notes.” It was refusing the enterprise vacuum — no Slack crawl, no org-wide agent, no service account — while still making messy human communication queryable years later. The journal is a dated evidence store with an ingest compiler and a retrieval policy, built by the operator who has to answer for the record.