Agenda Meetings
An agenda meeting is a structured solo working session: silicon-zack preps a grounded agenda, then bio-zack works it through by voice one item at a time β on the move β while the real work (filing tasks, writing artifacts) happens asynchronously between items.
It is deliberately not a recording. Capture belongs to the HiDock recorder.
Status: shipped 2026-07-21 as part of the iOS overhaul. Replaced a phone-side meeting-upload flow that was built and killed the same day.
Why the phone stopped trying to capture meetings
On the morning of 2026-07-21 an iOS MeetingRecorder shipped: record a
meeting on the phone, upload the m4a, run it through the existing diarized
ingest. It was deleted that afternoon on bio-zack's call β the phone will
never capture meetings. The recorder hardware owns capture, and a second path
into the meeting-ingest pipeline is split-brain by construction.
The lesson generalized into the pivot: the highest-leverage thing the phone can do for a meeting is not capture it but be the other participant β a grounded, pushing, hands-free thinking partner for solo work (planning a WBR, thinking through a decision) in exactly the moments bio-zack is walking or driving and can't sit at a keyboard.
Lifecycle
goal submitted (phone composer or POST /api/v1/agenda-meetings)
β
βΌ
status: preparing βββΆ agenda_prep agent (claude daemon / stream_inference)
β researches, produces 2-6 items, each with a
β compiled grounding_prompt: facts w/ sources,
β prior decisions, open questions, what to push on
βΌ
status: ready
β
βΌ
βββ per item βββββββββββββββββββββββββββββββββββββββββββββββββ
β POST .../items/{id}/start β
β βΌ β
β server mints ephemeral client secret (agenda_realtime.py) β
β βΌ β
β phone connects DIRECTLY to the realtime provider (WS) β
β full duplex, barge-in, live transcripts both sides β
β βΌ β
β model calls end_agenda_item (or manual end button) β
β βΌ β
β phone POSTs transcript β item: live β processing β
β βΌ β
β agenda_item_process agent: extracts decisions/actions AND β
β does the executable work β files Precedent tasks, writes β
β artifacts β outputs.md β done β
β βΌ β
β 3s interstitial, auto-advance to next item β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
agenda_wrap agent writes the summary
β
βΌ
everything posts to a per-meeting telegram topic
The realtime layer
Each item runs as one bounded duplex voice session. The server mints an
ephemeral client secret (gpt-realtime-2.1-mini, voice marin, 24 kHz PCM,
WebSocket subprotocol auth) with the persona, grounding pack, and tools baked
in server-side.
Two properties follow from that split, and both are deliberate:
- The phone never sees the prompt or the API key.
- The server never touches audio. Audio goes phone β provider directly.
The model gets exactly one tool β end_agenda_item β so "close that out" ends
the session naturally. A manual end button always exists.
Barge-in works through hardware AEC (.voiceChat audio session), so bio-zack
can interrupt the model mid-sentence the way he would a person.
The layer is provider-swappable on purpose: the server dispatches through
mint_realtime_session(provider=...) and iOS talks to a
RealtimeVoiceProviding protocol with a normalized RealtimeEvent. OpenAI can
become Gemini Live or xAI later. xAI is currently blocked β its ephemeral
tokens can't carry baked session config.
Why grounding is the whole product
An ungrounded voice model is a chatbot. The grounding_prompt compiled during
prep is what makes each item substantive: real facts with sources, decisions
already made, the open questions, and an explicit note on what to push back
on. bio-zack's profile explicitly rewards aggressive pushback β he gets to
insights that gentle probing misses β so the grounding pack tells the model to
argue.
Honest-failure design
Statuses only ever reflect live reality. This is the instrumentation-lying antibody applied to a multi-agent pipeline:
- any agent death, timeout, or empty result β
error+error_detail+ a telegram note - a startup orphan sweep (
sweep_orphans()) marks lostpreparing/processing/wrappingruns past their budget aserror - never a faked "done", never an infinite spinner
Where it lives
| Piece | Path |
|---|---|
| Router + prep/item/wrap runs + orphan sweep | api/src/routers/agenda_meetings.py |
| Agent prompts | api/src/services/agenda_prompts.py |
| Realtime session minting | api/src/services/agenda_realtime.py |
| Storage | api/src/services/agenda_store.py |
| On disk | data/agenda-meetings/<id>/{meeting.json, items/<item_id>/{transcript.md,outputs.md}} |
| iOS | Services/AgendaMeetings/*, Views/Meetings/* |
| Kickoff nudge | scripts/proactive/agenda_kickoff.py |
Signals
Working: bio-zack starts agenda meetings for real solo-thinking work and the grounded agent surfaces tension he hadn't pre-loaded; item-process agents produce genuinely filed tasks and written artifacts, not just summaries; the async auto-advance feels natural; errors surface honestly.
Failing: the voice agent feels like an ungrounded chatbot, or he'd rather just type.
Watch: whether a second realtime provider actually lands (that validates the swap seams); whether ~5 minutes of prep is worth the wait; resumability of a dropped mid-item session.
Related
- iOS App β the phone surface and voice mode
- Precedent & Todo Console β where item-process files tasks
- Telegram β per-meeting topics