Skip to content

Board Meetings

AI-facilitated board meetings where historical-figure personas (Stoic philosopher, investor, inventor, entrepreneur, TV host, president) deliberate on health and life decisions using Robert's Rules of Order. There is a pool of 7 personas, but a single meeting seats a smaller panel: the scheduled/direct path dynamically selects 5 attendees (chairman + 2 fixed members + 2 rotating members keyword-matched to the agenda). The system orchestrates parallel Claude CLI calls, manages voting, and produces formal resolutions with action items. Meetings run automatically on Sundays at 6AM PT via the scheduler, with a post-meeting secretary review emailed after completion.

Note: Two distinct entry paths set the attendee list differently. The scheduled cron path and the direct CLI runner (run_meeting_direct.py) call select_meeting_attendees() to seat 5. The interactive web-UI/API path defaults the meeting's attendees to all board members (get_all_board_members()) unless the create request supplies an explicit list. This page documents both.


Table of Contents

  1. How It Works
  2. Why This Design
  3. Agenda-Driven Attendee Selection
  4. Chairman DSL
  5. Trigger Points
  6. Meeting Flow
  7. Agent Personas
  8. Data Structures
  9. AI Facilitation Prompts
  10. File Locations
  11. Error Handling
  12. Known Limitations
  13. Improvement Opportunities
  14. Post-Meeting Secretary Review

How It Works

The board meeting system follows a structured lifecycle from creation to adjournment:

                              MEETING LIFECYCLE
                              =================

User creates meeting via web UI
        |
        v
+------------------+     +--------------------+
| POST /meetings   | --> | Chairman drafts    |
| (status:created) |     | agenda from goals, |
+------------------+     | health data        |
        |                +--------------------+
        v                         |
+------------------+              v
| AGENDA_DRAFT     | <-- ChatRoom conversation
| User reviews     |     with chairman (Marcus
| agenda with      |     can edit meeting.json
| chairman         |     directly via Edit tool)
+------------------+
        |
        v (approve)
+------------------+
| PREPARED         |
| Ready to start   |
+------------------+
        |
        v (start)
+------------------+
| IN_PROGRESS      |
+------------------+
        |
        v
+------------------+     +------------------+     +------------------+
| OPENING          | --> | QUORUM (Roll     | --> | Check: quorum    |
| Chairman calls   |     | Call via parallel|     | (3) of seated    |
| to order         |     | agent calls)     |     | members PRESENT  |
+------------------+     +------------------+     +------------------+
                                                          |
                                                          v
                         +--------------------------------+
                         |                                |
                         v                                |
              +-----------------------+                   |
              | FOR EACH AGENDA ITEM  | <-----------------+
              +-----------------------+
                         |
         +---------------+---------------+
         |                               |
         v (decision item)               v (non-decision)
+------------------+            +------------------+
| INTRO: Chairman  |            | INTRO + DISCUSS  |
| introduces topic |            | Round-robin      |
+------------------+            | then summary     |
         |                      +------------------+
         v
+------------------+
| DISCUSSION       |
| All agents share |
| perspectives     |
+------------------+
         |
         v
+------------------+
| MOTION phase     |
| Chairman drafts  |
| formal motion    |
| "RESOLVED, That.."
+------------------+
         |
         v
+------------------+
| SECOND phase     |
| Any member can   |
| second (excludes |
| proposer)        |
+------------------+
         |
         v (seconded)
+------------------+
| MOTION DISCUSSION|
| Focused on the   |
| motion text,     |
| amendments OK    |
+------------------+
         |
         v
+------------------+
| VOTING           |
| Parallel vote    |
| FOR/AGAINST/     |
| ABSTAIN with     |
| whisper mode     |
+------------------+
         |
         v
+------------------+
| ACTION_ITEMS     |
| Extract tasks    |
| with owners and  |
| deadlines        |
+------------------+
         |
         v
+------------------+
| Save Resolution  |
| RES-{mtg}-{item} |
+------------------+
         |
         v (next item or closing)
+------------------+
| CLOSING          |
| Chairman summary |
+------------------+
         |
         v
+------------------+
| ADJOURNMENT      |
| Formal close,    |
| email summary,   |
| Telegram notify  |
+------------------+

Why This Design

Robert's Rules of Order: Formal procedure creates structured debate and clear decision records. The motion/second/vote cycle ensures every decision has a paper trail.

Distinct Perspectives, Dynamically Seated: The 7-persona pool spans complementary philosophies (discipline vs. creativity, action vs. patience, systems vs. intuition, ambition vs. virtue). Rather than convene all 7 on every agenda, the scheduled path seats 5: a fixed chairman + 2 fixed members + 2 rotating members chosen to match the agenda's themes while avoiding philosophical overlap. See Agenda-Driven Attendee Selection for the mechanism. This keeps debate relevant to the topic and reduces wall-clock time and token cost.

Batched Parallel Execution: Agent calls are batched (2 at a time, batch_size=2 default in cli_runner.py) to avoid Claude API rate limits while keeping meetings reasonably fast. A full meeting with several agents and 3-5 agenda items takes roughly 15-30 minutes of wall-clock time. (The direct runner overrides batch_size per phase β€” e.g. roll call uses 5 β€” since fewer seated members fit under the rate ceiling.)

Quorum System: Prevents decisions when too many agents fail. Quorum is 3 for a 5-seat meeting (board_config.json: quorum, mirrored by run_meeting_direct.py: MIN_QUORUM = 3). If quorum is lost during roll call, the meeting cannot proceed.

Atomic Checkpointing: State is checkpointed after every phase transition via _sync_and_checkpoint(). This enables: - Pause/resume mid-meeting - Crash recovery from server restart - Session restoration with deterministic session IDs

Whisper Mode: Agents can include <thinking> tags for internal reasoning that isn't shown in the transcript. The <speaking> portion is what gets broadcast.

Direct File Editing: The chairman can edit meeting.json directly via the Edit tool during agenda review conversations, removing the need for special AGENDA_ACTION parsing.


Agenda-Driven Attendee Selection

Who sits on a given meeting is computed from the agenda. The mechanism lives in scripts/board/identity.py:select_meeting_attendees() and is driven by a small composition config.

Composition config

data/board/board_config.json is the single source of truth for who can attend and the quorum rules. identity.py falls back to a hard-coded _DEFAULT_CONFIG (identical to the shipped file) if it's missing.

{
  "chairman": "aurelius",
  "fixed_members": ["roosevelt", "dalio"],
  "rotating_pool": ["davinci", "jobs", "musk", "rogers"],
  "quorum": 3,
  "max_attendees": 5
}
Field Meaning
chairman Always seated; presides and votes.
fixed_members Always seated alongside the chairman.
rotating_pool Candidates for the 2 rotating seats, chosen per agenda.
quorum Minimum members responding PRESENT for the meeting to proceed.
max_attendees Seat cap (chairman + fixed + rotating).

The loader tolerates an older single fixed_member key for backward compatibility.

Selection algorithm

select_meeting_attendees(agenda_items) always returns [chairman, *fixed_members, rotating_1, rotating_2]:

  1. Concatenate agenda text β€” topic + description of every agenda item, lowercased.
  2. Score the pool β€” each rotating-pool member gets a point for every keyword in its AGENT_THEMES entry (scripts/board/context_pool.py) that appears in the agenda text.
  3. Pick the top scorer as the first rotating member (ties broken by member id).
  4. Apply a diversity penalty for the second pick: candidates that overlap philosophically with the first pick are penalized via OVERLAP_PAIRS, then the highest adjusted score wins.
# scripts/board/identity.py β€” diversity penalties applied to the 2nd rotating seat
OVERLAP_PAIRS = {
    frozenset({"musk", "roosevelt"}): 3,    # both bias toward speed/action
    frozenset({"aurelius", "rogers"}): 2,    # both bias toward patience/care
    frozenset({"dalio", "musk"}): 2,         # both bias toward systems/engineering
    frozenset({"jobs", "davinci"}): 2,       # both bias toward creativity/design
}

AGENT_THEMES is the keyword map that powers scoring (excerpt):

# scripts/board/context_pool.py
AGENT_THEMES = {
    "aurelius":  ["virtue", "duty", "discipline", "stoic", "patience"],
    "dalio":     ["efficiency", "data", "principles", "systems", "metrics"],
    "davinci":   ["innovation", "curiosity", "exploration", "creativity"],
    "jobs":      ["experience", "design", "simplicity", "user", "taste"],
    "musk":      ["ambition", "engineering", "scale", "first-principles", "speed"],
    "rogers":    ["empathy", "kindness", "emotional", "relationships", "patience"],
    "roosevelt": ["action", "courage", "vigor", "conservation", "boldness"],
}

Voting-eligibility model

Seating is config-driven; voting eligibility is read from each persona's JSON config:

Helper (scripts/board/identity.py) Reads Returns
get_chairman() board.role == "chairman" the chairman's member id
get_voting_members() board.voting == true all voting member ids
get_all_board_members() agents/board/*.json (skips _-prefixed) every persona id

Note: get_all_board_members() is what the interactive API path uses to default attendees β€” which is why an ad-hoc web meeting can seat all 7 while the scheduled path seats 5.


Chairman DSL

The chairman uses an embedded Domain-Specific Language (DSL) with structured commands wrapped in braces {}. Commands are embedded inline within natural language responses and parsed by scripts/board/dsl.py.

Core Concept

Instead of relying on unstructured LLM output, the chairman translates meeting actions into parseable commands:

"The motion has been seconded by Ray. Let us now vote on this resolution.

{SECOND: @dalio}
{PHASE: voting}
{DISCUSS}"

The natural language provides context for the transcript; the commands drive the meeting engine.

Command Reference

Meeting Opening

Command Syntax Description
ROLL {ROLL: @elon=present, @fred=present, @teddy=absent} Record attendance for all members
QUORUM {QUORUM} Announce that quorum has been established

Discussion Control

Command Syntax Description
DISCUSS {DISCUSS} Trigger full discussion round - all members speak or respond with "PASS"
CALL {CALL: @steve @elon} Call on specific members to speak

Motion Handling

Command Syntax Description
MOTION {MOTION: "RESOLVED, That..."} Propose a formal motion (must use "RESOLVED, That..." format)
SECOND {SECOND: @steve} Record who seconded the motion
AMENDMENT {AMENDMENT: @member "full amended text" "what changed"} Record an amendment with the proposer, new text, and summary
TABLE {TABLE} Defer the motion to a future meeting
WITHDRAW {WITHDRAW} Motion withdrawn by proposer

Voting & Dissent

Command Syntax Description
VOTE {VOTE: @steve=FOR, @elon=AGAINST "reason", @fred=ABSTAIN} Record votes with optional reasons
DISSENT {DISSENT: @elon "Formal dissent statement..."} Record a formal dissent for the record

Procedural

Command Syntax Description
PHASE {PHASE: voting} Announce a phase transition
RULING {RULING: "The point of order is well taken..."} Chairman's procedural ruling
NEXT {NEXT} Advance to next agenda item
OPEN {OPEN} Indicate floor is open for optional remarks (no action triggered)
ADJOURN {ADJOURN} End the meeting

Action Items

Command Syntax Description
ACTION {ACTION: "Task description" @owner "deadline"} Record an action item with owner and optional deadline

Command Parsing

Location: scripts/board/dsl.py

The parser uses regex to extract commands from the chairman's response:

# Pattern: {COMMAND: body} or {COMMAND}
COMMAND_PATTERN = re.compile(r'\{(\w+):\s*([^}]+)\}|\{(\w+)\}')

# Vote entries: @name=VOTE or @name=VOTE "reason"
VOTE_ENTRY = re.compile(r'@(\w+)=(\w+)(?:\s+"([^"]*)")?')

# Attendance entries: @name=present/absent
ATTENDANCE_ENTRY = re.compile(r'@(\w+)=(\w+)')

Each command is parsed into a typed dataclass:

@dataclass
class VoteCommand:
    votes: dict[str, tuple[str, str | None]]  # member -> (vote, reason)

@dataclass
class MotionCommand:
    text: str

@dataclass
class ActionCommand:
    task: str
    owner: str
    deadline: str | None

Chairman System Prompt

The chairman receives the DSL documentation in its system prompt (scripts/board/prompts.py:CHAIRMAN_SYSTEM):

You are the Chairman of the Board. You run formal board meetings
following Robert's Rules of Order.

## Your Role
- Open meeting with roll call and quorum check
- Guide discussion, call on members, interpret their responses
- Translate natural language into structured commands
- Ensure proper procedure (motion β†’ second β†’ discussion β†’ vote)
- Rule on points of order raised by members

## Command DSL
Use these commands inline in your responses:
{ROLL: @elon=present, @fred=present}
{DISCUSS}
{MOTION: "RESOLVED, That..."}
...

Meeting Flow with DSL

A typical agenda item flows like this:

  1. Introduction: Chairman introduces topic in natural language
  2. Discussion: {DISCUSS} triggers parallel agent calls, responses collected
  3. Motion: Chairman drafts motion using {MOTION: "RESOLVED, That..."}
  4. Seeking Second: {CALL: @dalio @jobs} or {DISCUSS} to find a second
  5. Recording Second: {SECOND: @dalio} when someone seconds
  6. Final Discussion: Another {DISCUSS} for focused debate on the motion
  7. Voting: {PHASE: voting} then {VOTE: @steve=FOR, @elon=AGAINST "reason"}
  8. Action Items: {ACTION: "Task" @shareholder "2026-01-15"}

Why a DSL?

  1. Reliability: Structured commands are easier to parse than natural language
  2. Auditability: Commands create a clear log of procedural actions
  3. Flexibility: Chairman can combine natural language context with structured actions
  4. Consistency: Same command format across all meeting types

Trigger Points

Entry Point Location Method
Create meeting POST /api/v1/board/meetings Creates meeting with initial status
Request agenda POST /api/v1/board/meetings/{id}/request-agenda Chairman drafts agenda from context
Chat with chairman POST /api/v1/board/meetings/{id}/conversation Live chat during agenda review
Approve agenda POST /api/v1/board/meetings/{id}/approve-agenda Locks agenda, status -> PREPARED
Edit agenda POST /api/v1/board/meetings/{id}/edit-agenda Return to AGENDA_DRAFT status
Revise agenda POST /api/v1/board/meetings/{id}/revise-agenda Chairman revises based on feedback
Start meeting POST /api/v1/board/meetings/{id}/start Launches orchestrator task
Pause meeting POST /api/v1/board/meetings/{id}/pause Pauses at next checkpoint
Resume meeting POST /api/v1/board/meetings/{id}/resume Resumes from state.json
User input POST /api/v1/board/meetings/{id}/user-input Queue input for next checkpoint
Get transcript GET /api/v1/board/meetings/{id}/transcript Read meeting transcript
Get summary GET /api/v1/board/meetings/{id}/summary Executive summary (generates on demand)
Regenerate summary POST /api/v1/board/meetings/{id}/summary/regenerate Force regenerate, optionally email
Summary feedback PUT /api/v1/board/meetings/{id}/summary/feedback Add feedback to summary
Delete meeting DELETE /api/v1/board/meetings/{id} Delete meeting and all data
WebSocket updates WS /api/v1/board/meetings/{id}/ws Real-time transcript & phase changes
Broadcast (internal) POST /api/v1/board/meetings/{id}/broadcast CLI scripts send WS updates
List resolutions GET /api/v1/board/resolutions All resolutions, filter pending_only
Get resolution GET /api/v1/board/resolutions/{id} Single resolution detail
Delete resolution DELETE /api/v1/board/resolutions/{id} Delete a resolution
Respond to resolution POST /api/v1/board/resolutions/{id}/respond Accept/reject/defer resolution
List action items GET /api/v1/board/actions Actions across all resolutions
List members GET /api/v1/board/members All board member profiles (the 7-persona pool)
Get member GET /api/v1/board/members/{id} Single board member profile
Agenda item commentary PUT /api/v1/board/meetings/{id}/agenda/items/{item_id}/commentary Add user notes to item
Agenda feedback PUT /api/v1/board/meetings/{id}/agenda/feedback Overall feedback, request revision
Clear conversation DELETE /api/v1/board/meetings/{id}/conversation Reset chairman chat

CLI Scripts: Two end-to-end runners exist.

  • scripts/board/run_meeting_direct.py β€” bypasses the API entirely, building the meeting in-process. It calls select_meeting_attendees() to seat 5, runs all phases, sends Telegram updates, broadcasts to WebSocket clients, and finishes with a secretary review (see Post-Meeting Secretary Review). This is the path the scheduler uses.
  • scripts/board/run_meeting.py β€” drives a meeting through the API (httpx calls to create β†’ request/approve agenda β†’ start β†’ poll), useful for exercising the real HTTP surface.

No /board CLI command or skill exists. Interactive meetings are initiated via the web UI at /board; automated meetings run via the scheduler.


Meeting Flow

Phase Sequence

For decision items (type: decision):

OPENING -> QUORUM -> [INTRO -> DISCUSSION -> MOTION -> SECOND -> MOTION_DISCUSSION -> VOTING -> ACTION_ITEMS]* -> CLOSING -> ADJOURNMENT

For non-decision items (type: review, discussion, information):

OPENING -> QUORUM -> [INTRO -> DISCUSSION (round-robin) -> summary]* -> CLOSING -> ADJOURNMENT

Phase Details

"All members" below means all seated attendees (5 on the scheduled path, up to 7 on an ad-hoc API meeting).

Phase What Happens Agent Calls Checkpoint
OPENING Chairman calls meeting to order 1 call to chairman Yes
QUORUM Roll call - all members respond "PRESENT" Parallel call to all members (batched) Yes
INTRO Chairman introduces agenda item 1 call to chairman Yes
DISCUSSION All members share perspectives (whisper mode) Parallel call to all members (batched) Yes
MOTION Chairman drafts formal motion (with retry) 1-2 calls Yes
SECOND Seek second from any voting member Parallel call to non-proposers (excludes proposer) Yes
MOTION_DISCUSSION Focused discussion, amendments possible Parallel call to all members + amendment prompt Yes
VOTING Formal vote collection (whisper mode) Parallel call to all members (batched) After each vote
ACTION_ITEMS Extract tasks from adopted motion 1 call to chairman Yes
CLOSING Chairman summarizes decisions 1 call to chairman Yes
ADJOURNMENT Formal meeting close, notifications sent 1 call to chairman Yes

State Machine

class BoardMeetingStatus(str, Enum):
    CREATED = "created"           # Initial state after POST /meetings
    AGENDA_DRAFT = "agenda_draft" # Chairman drafting/reviewing agenda
    PREPARED = "prepared"         # Agenda approved, ready to start
    IN_PROGRESS = "in_progress"   # Meeting actively running
    PAUSED = "paused"             # Meeting paused at checkpoint
    COMPLETED = "completed"       # Meeting finished successfully
    FAILED = "failed"             # Meeting error (recoverable)
    QUORUM_LOST = "quorum_lost"   # Not enough agents responding

Agent Personas

The full pool is 7 personas, each with a distinct perspective loaded from agents/board/{member_id}.json. The chairman and the two fixed members are always seated; the other four form the rotating_pool that the selection algorithm draws two from per meeting.

Agent ID Display Name Role Perspective
aurelius Marcus Aurelius Chairman Stoic philosophy - virtue, duty, long-term thinking, accepts what cannot be changed
dalio Ray Dalio Member Principles-based decisions, radical transparency, systematic thinking, data-driven
davinci Leonardo da Vinci Member Polymath thinking, curiosity, cross-domain synthesis, creative exploration
jobs Steve Jobs Member Simplicity, user experience, design thinking, taste and intuition
musk Elon Musk Member First principles, ambitious goals, engineering focus, speed over perfection
rogers Fred Rogers Member Emotional intelligence, kindness, patience, relationship focus
roosevelt Teddy Roosevelt Member Bold action, vigor, conservation, courage over caution

Agent Configuration Structure

{
  "v": 1,
  "id": "board-aurelius",
  "name": "Marcus Aurelius",
  "description": "Chairman of the Board - Stoic philosophy and virtue ethics",
  "extends": "base",
  "launchable": false,
  "tags": ["board"],
  "board": {
    "role": "chairman",
    "display_name": "Marcus Aurelius",
    "voting": true,
    "perspective": "Stoic philosophy emphasizing virtue, duty..."
  },
  "claude_md": {
    "sections": [
      {
        "heading": "## Your Identity",
        "content": "You are Marcus Aurelius, Roman Emperor...",
        "position": "start"
      },
      {
        "heading": "## Parliamentary Procedure (Robert's Rules)",
        "content": "This board follows Robert's Rules of Order...",
        "position": "after:## Your Identity"
      },
      {
        "heading": "### Chairman Duties",
        "content": "As Chairman, you have specific responsibilities...",
        "position": "after:## Parliamentary Procedure"
      }
    ]
  }
}

Facilitator Fallback Chain

If the chairman (aurelius) fails health check, the system falls back to: 1. dalio (systems thinker - procedural backup) 2. davinci (creative adapter - tertiary backup)

Each facilitator is tested with a 30-second health check before selection.


Data Structures

BoardMeeting

attendees is whatever the entry path computed: the 5 ids from select_meeting_attendees() on the scheduled/direct path, or the all-pool default below on an ad-hoc API meeting.

// data/board/meetings/mtg-{id}/meeting.json
{
  "id": "mtg-9fed5d5a",
  "name": "Weekly Health Review",
  "scheduled_start": null,
  "status": "agenda_draft",
  "attendees": ["aurelius", "dalio", "davinci", "jobs", "musk", "rogers", "roosevelt"],
  "agenda": {
    "meeting_id": "mtg-9fed5d5a",
    "status": "draft",
    "items": [
      {
        "id": "agenda-1",
        "topic": "Goal Structure and Early Momentum",
        "type": "decision",
        "time_minutes": 15,
        "description": "We enter 2026 with 12 active goals...",
        "desired_outcome": "Decide on a framework for tracking...",
        "user_commentary": ""
      }
    ],
    "standing_items": ["Opening remarks", "Closing remarks"],
    "user_feedback": "",
    "revision_requested": false
  },
  "created_at": "2026-01-03T22:17:39.174118",
  "started_at": null,
  "completed_at": null
}

MeetingState (Checkpoint)

// data/board/meetings/mtg-{id}/state.json
{
  "meeting_id": "mtg-abc123",
  "status": "in_progress",
  "current_agenda_index": 2,
  "current_phase": "discussion",
  "votes_collected": {
    "aurelius": "FOR",
    "dalio": "FOR",
    "musk": "AGAINST"
  },
  "vote_reasons": {
    "aurelius": "Aligns with our long-term health goals",
    "dalio": "The data supports this intervention",
    "musk": "Timeline is too conservative"
  },
  "vote_dissents": {
    "musk": "We should move faster on implementation"
  },
  "vote_announced": false,
  "resolutions_saved": ["RES-mtg-abc123-1"],
  "agent_sessions": {
    "aurelius": "a1b2c3d4e5f67890",
    "dalio": "b2c3d4e5f6789012"
  },
  "present_members": ["aurelius", "dalio", "davinci", "jobs", "musk", "rogers", "roosevelt"],
  "transcript": [
    ["Marcus Aurelius", "This meeting is now called to order.", "2026-01-03T10:00:00"],
    ["Ray Dalio", "PRESENT", "2026-01-03T10:00:15"]
  ],
  "last_checkpoint_ts": "2026-01-03T10:15:30.123456",
  "error_count": 0,
  "user_input_pending": []
}

Resolution

// data/board/resolutions/RES-mtg-abc123-1.json
{
  "id": "RES-mtg-abc123-1",
  "meeting_id": "mtg-abc123",
  "agenda_item_id": "agenda-1",
  "topic": "Increase Weekly Cardio",
  "motion": {
    "id": "MOT-mtg-abc123-agenda-1",
    "text": "RESOLVED, That the shareholder shall increase weekly cardio sessions from 2 to 3, targeting a minimum of 30 minutes per session, effective immediately.",
    "proposed_by": "aurelius",
    "seconded_by": "dalio",
    "status": "adopted",
    "original_text": null,
    "created_at": "2026-01-03T10:30:00"
  },
  "vote": {
    "for": ["aurelius", "dalio", "rogers", "roosevelt"],
    "against": ["musk"],
    "abstain": ["jobs", "davinci"]
  },
  "dissents": [
    {
      "member_id": "musk",
      "member_name": "Elon Musk",
      "statement": "Three sessions is not ambitious enough.",
      "recorded_at": "2026-01-03T10:45:00"
    }
  ],
  "action_items": [
    {
      "id": "ACT-12345678",
      "task": "Schedule 3 cardio sessions per week on calendar",
      "owner": "Shareholder",
      "deadline": "2026-01-10",
      "resolution_id": "RES-mtg-abc123-1",
      "status": "pending"
    }
  ],
  "user_response": null,
  "created_at": "2026-01-03T10:50:00"
}

ConversationHistory

// data/board/meetings/mtg-{id}/conversation.jsonl (one JSON per line)
{"id": "msg-abc123", "role": "user", "content": "Can we add a sleep goal?", "timestamp": "..."}
{"id": "msg-def456", "role": "assistant", "content": "Certainly. I've added...", "timestamp": "...", "agenda_action": null}

AI Facilitation Prompts

Chairman System Prompt (with DSL)

Location: scripts/board/prompts.py:CHAIRMAN_SYSTEM

The chairman receives a system prompt that defines: - Their role as meeting facilitator - The complete DSL command reference (see Chairman DSL) - Meeting flow expectations - Procedural guidelines

This is the core prompt that enables structured meeting control. See the Chairman DSL section for the full command reference.

Agenda Generation Prompt

Location: scripts/board/agenda.py:build_agenda_prompt()

The agenda prompt combines multiple data sources:

Draft an agenda for the upcoming board meeting.

**Available Context:**

## Active Goals (Raw)
{loaded from goals/active/*.json with progress percentages}

## Goal Progress Analysis (Memory View)
{from memory/views/goal-progress.json - status breakdown, attention needed}

## Recent Garmin Data (past 3 days)
{sleep hours, HRV, steps, body battery ranges}

## Recent Check-ins (past 7 days)
{energy, mood, key responses}

## Health Snapshot
{from memory/views/health-snapshot.json}

## Training Status
{from memory/views/training-status.json}

## Week Summary
{from memory/views/week-summary.json}

## Pending Resolutions (awaiting user response)
{resolutions without user_response}

**Instructions:**
Create 3-5 agenda items covering the most important topics.
Consider: goal progress reviews, health patterns, training compliance.

**Format each item as:**
1. [TOPIC] (type: review|discussion|decision)
   Description: What we need to discuss
   Desired outcome: What decision or action we want

Motion Generation Prompt

Location: api/src/services/board.py:_generate_motion()

The board needs a motion on: {topic}

{description}

Draft a formal motion. Your response must include:
RESOLVED, That <specific person/role> shall <concrete action> by <specific date or "immediately">.

Example: "RESOLVED, That the user shall establish a 7-hour minimum sleep target effective immediately."

Be SPECIFIC - use real names, concrete actions, and actual deadlines. Do NOT use placeholder brackets.

If the first attempt fails format validation (no RESOLVED, contains [brackets], too short), a retry prompt is sent with explicit format requirements.

Vote Collection Prompt

Location: api/src/services/board.py:_collect_formal_vote()

The vote has been called on:

{motion in box format}

Cast your vote in this EXACT format:
VOTE: FOR (or AGAINST or ABSTAIN)
REASON: Your 1-2 sentence explanation for the record
DISSENT: If voting AGAINST, state your formal objection for the record

Remember: Your vote is on the motion as written above.

{WHISPER_WRAPPER - allows <thinking> tags for internal reasoning}

Chairman Conversation Prompt

Location: scripts/board/chairman_conversation.py:_build_conversation_prompt()

You are Marcus Aurelius, Chairman of the Personal Board of Directors.

You are having a conversation with the user about the meeting agenda. Your role is to:
1. Explain your reasoning for agenda items when asked
2. Answer questions about priorities and timing
3. Make changes to the agenda based on feedback
4. Suggest improvements when appropriate

## Current Agenda
{agenda_json}

## Context Summary
{goals and health summary from memory views}

## Conversation History
{last 10 messages}

## User's Message
{user_message}

## Instructions
Respond conversationally. Be concise and stoic.

## Modifying the Agenda
You have full authority to modify the agenda. The meeting file is at:
{meeting_file_path}

When you need to modify the agenda, use the Edit tool to update the meeting.json file directly.

File Locations

Purpose Path Format
API Router api/src/routers/board.py FastAPI endpoints (prefix /api/v1/board)
Orchestrator api/src/services/board.py Meeting execution logic (~2577 lines)
Schemas api/src/schemas/board.py Pydantic models (~449 lines)
Agent Configs agents/board/*.json 7 persona definitions (the pool)
Composition Config data/board/board_config.json Chairman, fixed/rotating members, quorum, max attendees
CLI Runner scripts/board/cli_runner.py Claude subprocess execution, MIN_QUORUM, batching
Context Pool scripts/board/context_pool.py Agent context assembly, AGENT_THEMES, dissenter selection
Agenda Builder scripts/board/agenda.py Agenda prompts and parsing
Chairman DSL scripts/board/dsl.py Command parser (VOTE, MOTION, DISCUSS, etc.)
Resolution Parser scripts/board/resolution.py Vote parsing logic
Chairman Chat scripts/board/chairman_conversation.py Agenda review conversation
Session Manager scripts/board/session_manager.py Persistent session tracking
Turn Manager scripts/board/turn_manager.py @mention routing
Identity Loader scripts/board/identity.py Load agent configs, attendee selection, voting model
Context Generator scripts/board/context.py Generate board book
Summary Generator scripts/board/summary.py Executive summary + email
Secretary Review scripts/board/secretary_review.py Post-meeting triage + emailed summary
Action-Item Ledger scripts/board/action_items.py Silicon-zack action-item tracker (consumed by /sleep)
Resolution Tracking scripts/board/resolution_tracking.py Cross-meeting resolution/action status
Data Validation scripts/board/validation.py Pre-meeting state vs. raw-data validation
Dashboard Builder scripts/board/build_dashboard.py Resolution-tracking HTML dashboard
Prompts scripts/board/prompts.py WHISPER_WRAPPER and others
Direct Runner scripts/board/run_meeting_direct.py Bypass API, run meeting (scheduler path)
API Runner scripts/board/run_meeting.py Drive a meeting through the HTTP API
Scheduled Runner (legacy) scripts/board/scheduled_meeting.sh Shell wrapper; no longer the scheduler's path
Meeting Data data/board/meetings/mtg-{id}/ Per-meeting files
Resolutions data/board/resolutions/RES-*.json Saved resolutions
Action Items data/board/action_items.json Action-item ledger store
Resolution Tracking data/board/resolution_tracking.json Cross-meeting tracking store
Chairman Chat UI web/src/components/board/ChairmanChatPanel.tsx React chat component

Error Handling

Quorum System

# scripts/board/cli_runner.py
MIN_QUORUM = 2  # minimum agents for valid meeting decisions (small-board floor)

def check_quorum(results: dict[str, str | Exception]) -> bool:
    successful = sum(1 for r in results.values() if isinstance(r, str))
    return successful >= MIN_QUORUM

If quorum is lost during roll call, meeting status becomes QUORUM_LOST and cannot proceed. A QuorumError exception is raised.

Note: There are several quorum knobs and they don't all agree β€” verify against the path you're running. cli_runner.py:MIN_QUORUM = 2 (the low-level check), run_meeting_direct.py:MIN_QUORUM = 3, and board_config.json:quorum = 3 (the config the scheduled path honors). The unit test scripts/board/test_board.py::test_min_quorum_value still asserts == 4 and is stale against the shipped value β€” treat 3 as the effective quorum for a 5-seat meeting.

Agent Retry Logic

Location: api/src/services/board.py:_agent_call_with_retry()

  • Up to 2 retries per agent call
  • Exponential backoff between retries (2^attempt seconds)
  • Uses persistent sessions when available (run_board_agent_with_session)
  • Falls back to stateless calls if session unavailable

Vote Parsing

Location: scripts/board/resolution.py:parse_vote_response()

  • Looks for exact VOTE: FOR|AGAINST|ABSTAIN format first
  • Falls back to heuristic keyword matching if exact format not found
  • Tracks heuristic_count to warn if too many votes required fallback parsing
  • Failed agents during voting default to ABSTAIN

Motion Parsing

  • Looks for RESOLVED, That... or I move that... formats
  • Rejects motions with unfilled template brackets [like this]
  • Retry prompt sent if first attempt fails validation
  • If second attempt fails, agenda item is skipped gracefully

Crash Recovery

  1. State checkpointed after every phase transition via _sync_and_checkpoint()
  2. Atomic writes: tmp file + os.replace() pattern
  3. On API startup, recover_interrupted_meetings() scans for IN_PROGRESS or PAUSED meetings
  4. Restores context pool with transcript from state.json
  5. Session IDs are deterministic (MD5(meeting_id:member_id)) for resumption
  6. Resume starts from current_agenda_index, skipping already-processed items

Known Limitations

Performance Bottlenecks

  1. Batch Size = 2: Rate limiting means parallel calls default to 2 agents at a time (cli_runner.py). A full meeting with several seated agents and 3-5 agenda items can take 15-30 minutes of wall-clock time.

  2. 120-Second Timeout: Per-agent call timeout. Long-winded responses may get truncated.

  3. No Streaming to UI: Responses are collected in full before display. WebSocket infrastructure exists but streaming isn't implemented.

Prompt Issues

  1. Motion Format Compliance: Agents sometimes ignore the exact RESOLVED, That... format, requiring retry logic. Sometimes include [placeholder] brackets.

  2. Vote Parsing Heuristics: When agents don't use exact VOTE: FOR format, fallback parsing uses keyword matching which can misinterpret nuanced responses. High heuristic parse rates (>50%) trigger warnings.

  3. Whisper Mode Parsing: The <thinking> and <speaking> tag extraction is regex-based and can fail on malformed output.

Missing Features

  1. No CLI Command: Must use the web UI or run_meeting_direct.py to create meetings. A /board slash command would be useful.

  2. No Amendment Voting: Friendly amendments are accepted by chairman discretion only. Contested amendments could use a formal amendment process.

  3. Vote-Format Compliance: Agents still sometimes ignore the exact VOTE: FOR / RESOLVED, That... formats, leaning on retry logic and heuristic parsing.

Resolution & Action-Item Follow-up

Action items and resolutions are tracked across meetings β€” the older "not synced / no historical analysis" limitations are resolved:

  • Action-item ledger (scripts/board/action_items.py): a silicon-zack action-item tracker (list / add / complete / extract <meeting_id> / scope subcommands) stored at data/board/action_items.json. extract_from_meeting() pulls {ACTION: "task" @owner "deadline"} commands out of a meeting transcript; scope_for_sleep() buckets pending items into overdue / upcoming-week / open-ended for the executive /sleep cycle to scope work and schedule proactive messages.
  • Cross-meeting resolution tracking (scripts/board/resolution_tracking.py): sync_from_resolutions() scans resolution files + the action ledger and maintains data/board/resolution_tracking.json, surfacing execution status for follow-up in later agendas (stale after STALE_DAYS = 30).
  • Dashboard (scripts/board/build_dashboard.py): renders an HTML resolution-tracking dashboard from the ledger, resolutions, and sleep-gate status.

Scheduling

Weekly meetings run automatically on Sundays at 6AM PT (api/src/scheduler.py: BOARD_MEETING_DAY = 6, BOARD_MEETING_HOUR = 6, BOARD_MEETING_MINUTE = 0, scheduled in _board_meeting_loop()). The loop's callback calls api/src/services/scheduler_followups.py:run_board_meeting(), which subprocess-execs python scripts/board/run_meeting_direct.py with a 1800-second (30-minute) timeout and reports completed / failed / timeout. The scheduler sends a Telegram notification on failure. The scripts/board/scheduled_meeting.sh shell wrapper still exists on disk but is no longer the scheduler's execution path. Ad-hoc meetings can still be created via the web UI.


Improvement Opportunities

High Value

  1. Streaming Responses: WebSocket infrastructure already exists via ConnectionManager. Could stream agent output as it arrives rather than waiting for full response.

  2. External Task-System Sync: Extracted action items already land in the silicon-zack ledger (action_items.py) and feed /sleep. A further step would push items owned by bio-zack into Precedent automatically using their owner and deadline.

  3. Goal-Drift Meeting Triggers: Weekly meetings already run on schedule. Could add ad-hoc meetings triggered by goal drift detection from /insights when multiple goals are off track.

Medium Value

  1. Designated Dissenter: context_pool.py has select_dissenter() function that picks an agent to play devil's advocate based on motion themes - currently unused. Could create more balanced debate.

  2. Turn Management: turn_manager.py supports @mention routing and speaker queues but isn't fully integrated into meeting flow. Would enable more dynamic conversation.

  3. Parallel Session Init: Could initialize all agent sessions in parallel during OPENING phase instead of on-demand. Would reduce latency in later phases.

  4. Resolution Templates: Common motion patterns (increase/decrease something, establish routine, review progress) could have templates to reduce format compliance issues.

Technical Debt

  1. Vote Parsing Robustness: High heuristic parse rates suggest the vote format instructions aren't clear enough. Could add examples or structured output.

  2. Consistent Whisper Mode: Not all phases use whisper mode. Standardizing would give more insight into agent reasoning.

  3. Meeting Duration Tracking: Currently no tracking of actual meeting duration vs. estimated time. Would help calibrate agenda time estimates.


Post-Meeting Secretary Review

After a meeting completes (run_meeting_direct.py Phase 5), silicon-zack acts as secretary: instead of emailing the raw transcript, it reads the transcript + resolutions and triages every action item into three buckets, then sends a structured summary. Entry point: scripts/board/secretary_review.py:review_and_send(meeting_id).

Bucket Meaning
did_it Reversible, < ~15 min, no judgment needed β€” already handled.
recommending Low-stakes judgment call β€” proposed, awaiting a nod.
need_your_call Irreversible / high stakes β€” explicitly kicked to bio-zack.

The review is persisted to data/board/meetings/mtg-{id}/review.json and emailed (subject Board Review: {meeting_name}) to bio-zack via mail.fastmail.send_email (recipient falls through to the configured default β€” not hardcoded). If the email fails, the runner falls back to a plain summary. This bucket-based triage is the mechanism by which board output becomes actionable without dumping raw deliberation on the reader.


Quick Reference: Running a Board Meeting

Via Web UI

  1. Navigate to /board
  2. Click "New Meeting"
  3. Enter meeting name
  4. Wait for chairman to draft agenda
  5. Review agenda, chat with chairman to modify
  6. Click "Approve Agenda"
  7. Click "Start Meeting"
  8. Monitor transcript via WebSocket
  9. Respond to resolutions after meeting

Via Direct Script

cd /path/to/vita
PYTHONPATH=/path/to/vita uv run python scripts/board/run_meeting_direct.py

This runs a "Weekly Health & Productivity Review"-style meeting: it builds an agenda, calls select_meeting_attendees() to seat 5, runs all phases, sends Telegram updates at key moments, broadcasts to any connected WebSocket clients, and ends with the secretary review email.

# inspect / manage the action-item ledger produced by meetings
uv run python scripts/board/action_items.py list
uv run python scripts/board/action_items.py scope            # buckets for /sleep
uv run python scripts/board/action_items.py extract mtg-xxx  # pull {ACTION:} from a transcript

# refresh cross-meeting tracking + the HTML dashboard
uv run python scripts/board/build_dashboard.py

Where to go next

  • Executive Loop β€” the /sleep cycle that consumes the board's action-item ledger.
  • Precedent β€” the task/habit system action items could sync into.
  • Scheduling β€” how the Sunday board-meeting cron is registered.
  • Telegram β€” where in-meeting updates and failure notifications land.
  • Model Guidance β€” board_meeting runs unpinned (no fixed model); routing is defined there.

For technical architecture details, see the inline comments in api/src/services/board.py (~2577 lines of orchestration logic).