12 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build & Test Commands
# Install in development mode
pip install -e ".[dev]"
# Run all tests
pytest
# Run a single test file
pytest tests/test_markers.py
# Run a single test
pytest tests/test_markers.py::TestExtractVote::test_ready_vote
# Run with coverage
pytest --cov=discussions
# Test SmartTools directly (Unix philosophy - test tools independently)
cat examples/brainstorm_notification_system.md | discussion-parser | jq .
cat examples/brainstorm_notification_system.md | discussion-parser | discussion-vote-counter
# Find orphaned diagrams (not referenced by any discussion)
discussions cleanup
# Delete orphaned diagrams with confirmation
discussions cleanup --delete
Architecture
Orchestrated Discussions is a multi-agent AI discussion orchestrator that manages structured conversations between AI personas with voting and phase-based workflows.
Project Ecosystem
This is the middle layer of a three-project stack:
- SmartTools - AI provider abstraction and tool execution (dependency)
- Orchestrated Discussions (this) - Conversation orchestration
- CascadingDev - Git-driven automation (depends on this)
Architectural Philosophy: Unix Philosophy & Pipes and Filters
This project strictly follows the Unix Philosophy. This is non-negotiable.
Core Principles
- Each tool does one thing well - SmartTools are self-contained, complete units
- Tools communicate via stdin/stdout - JSON flows through pipes
- Tools are composable - Any tool's output can be another's input
- No tool knows about another's internals - Only the interface (stdin/stdout/args)
- The discussion file is the source of truth - Tools read and write to it
Architecture Rules
NEVER:
- Import SmartTools internals (
from smarttools.providers import ...) - Duplicate logic that exists in a SmartTool
- Build "helper functions" when you should make a SmartTool
- Call AI providers directly - that's what SmartTools are for
- Put orchestration logic inside a tool (tools are stateless)
ALWAYS:
- Call SmartTools via subprocess:
subprocess.run(["tool-name"], input=data, ...) - Pass data through stdin, receive through stdout
- Keep Python layer thin - it only orchestrates, never implements
- Each SmartTool must be independently testable:
cat file | tool | jq . - Create a new SmartTool rather than adding complexity to existing code
Anti-Pattern Examples
# WRONG - Importing SmartTools internals, bypassing the tool
from smarttools.providers import call_provider
prompt = build_prompt_in_python(discussion) # Logic that belongs in SmartTool
result = call_provider("claude", prompt)
# RIGHT - Calling the SmartTool via subprocess
result = subprocess.run(
["discussion-architect", "--callout", callout],
input=discussion_content,
capture_output=True,
text=True
)
response = json.loads(result.stdout)
# WRONG - Duplicating vote counting logic in Python
def count_votes(discussion):
ready = sum(1 for v in votes if v == "READY")
# ... reimplementing what discussion-vote-counter does
# RIGHT - Using the SmartTool
result = subprocess.run(
["discussion-vote-counter"],
input=parser_output,
capture_output=True,
text=True
)
consensus = json.loads(result.stdout)
Why This Matters
Linux follows these principles and is arguably the most powerful and stable software system ever created. Benefits:
- Debuggability: Test any tool in isolation:
cat test.md | discussion-parser | jq . - Flexibility: Swap implementations without changing orchestration
- Reliability: Each tool can be hardened independently
- Composability: Build new workflows by combining existing tools
- Maintainability: Change one tool without breaking others
The Python Layer's Role
The Python code in this project (runner.py, cli.py, ui/) is orchestration only:
- Decide which tools to call and in what order
- Pass data between tools (pipe stdout to stdin)
- Handle errors and retries
- Provide user interface
It should never contain:
- AI prompt construction (that's in SmartTool configs)
- Vote counting logic (that's
discussion-vote-counter) - Response parsing logic (that's in each SmartTool's code steps)
- Discussion format knowledge (that's
discussion-parser)
Key Design Decision: Participants ARE SmartTools
Participants are implemented as SmartTools, not a separate system. Each participant lives in ~/.smarttools/discussion-{alias}/config.yaml and can be:
- Invoked directly for testing:
cat discussion.md | discussion-architect --callout "..." - Edited via SmartTools TUI for prompt debugging
- Created via
discussions participants addwizard (generates SmartTool config)
This means:
- No duplicate participant definition system
- Full SmartTools features available (multi-step pipelines, code steps, provider fallbacks)
- Independent testing and debugging of each participant
Core Abstractions
-
Discussion (
discussion.py): A markdown file with metadata headers, context, and comment blocks. Append-only - content grows but never shrinks. State stored in HTML comments (<!-- Key: value -->). -
Participant: A SmartTool in
~/.smarttools/discussion-{alias}/. Discovered by naming convention. Has personality prompt, expertise metadata, and vote behavior. Two types:votingandbackground. -
Markers (
markers.py): Structured annotations parsed from comment text:VOTE: READY|CHANGES|REJECTQ:/QUESTION:- QuestionsTODO:/ACTION:- Action itemsDECISION:- Recorded decisionsCONCERN:- Raised concernsDIAGRAM:- Diagram file references (e.g.,DIAGRAM: diagrams/flow.puml)@alias- Mentions
-
Voting (
voting.py): Consensus calculation with configurable thresholds. -
Runner (
runner.py): Invokes participant SmartTools via subprocess, parses JSON responses, appends to discussion file.
Discussion File Format
<!-- DISCUSSION -->
<!-- Title: Feature Name -->
<!-- Phase: initial_feedback -->
<!-- Status: OPEN -->
<!-- Template: feature -->
<!-- Participants: architect, security, pragmatist -->
# Title
## Context
...
---
Name: AI-Architect
Comment content with markers...
VOTE: CHANGES
---
Comment blocks are delimited by --- and start with Name: AuthorName.
Template System
Templates define phase-specific behavior for discussions. Stored in templates/ as YAML files.
# templates/feature.yaml
phases:
initial_feedback:
goal: Gather diverse perspectives
instructions: |
- Focus on feasibility and risks
- Raise blocking issues early
voting: false
next_phase: detailed_review
How it works:
- Discussion references template via
<!-- Template: feature --> - Participant SmartTools read the template file to get phase context
- AI prompts include phase goal and instructions for context-aware responses
Participants use --templates-dir argument (default: templates) to locate template files.
Consensus Logic
VotingConfig.threshold_ready(default 0.67): Fraction of READY votes neededVotingConfig.threshold_reject(default 0.01): Any REJECT blocks by defaultVotingConfig.human_required(default True): Needs human READY vote- Human participants detected by name NOT starting with
ai_,ai-,bot_,bot-
SmartTools Integration
Orchestrated Discussions (thin orchestration layer)
│ invokes via subprocess
▼
SmartTools (discussion-* tools)
│
├── Utility tools (code-only):
│ - discussion-parser → JSON structured data
│ - discussion-validator → validation results
│
├── Orchestration tools (code-only):
│ - discussion-vote-counter → consensus calculation
│ - discussion-mention-router → participant routing
│ - discussion-status-promoter → status transitions
│ - discussion-turn-appender → append responses
│ - discussion-config → modify metadata
│
├── Utility tools (AI):
│ - discussion-summarizer → .sum.md files
│
└── Participant tools (AI):
- discussion-moderator, discussion-architect, etc.
│ uses providers
▼
AI Providers (claude, codex, gemini, etc.)
Everything is a SmartTool - parsing, validation, orchestration, summarization, and participants. This means:
- All components testable independently:
cat discussion.md | discussion-parser | jq . - Full pipelines runnable manually:
cat d.md | discussion-parser | discussion-vote-counter | jq . - Debuggable via SmartTools TUI
- Composable: tools can call other tools
Manual Turn Execution
# Parse → route → call participants → append → count votes → promote status
DISCUSSION="feature.md"
STATE=$(cat "$DISCUSSION" | discussion-parser)
ROUTING=$(echo "$STATE" | discussion-mention-router)
# ... call each participant, collect responses ...
# ... append responses, count votes, promote status ...
See scripts/run-turn.sh for a complete example.
Project Files
docs/DESIGN.md- Full architecture and SmartTool specificationsdocs/IMPLEMENTATION.md- Phased implementation plansmarttools/- Bundled SmartTool configs (copied to ~/.smarttools/ on install)templates/- Discussion workflow templates (phase definitions)examples/- Example discussion files for testingscripts/run-turn.sh- Manual turn orchestration script
Participants respond with JSON: {"comment": "...", "vote": "READY|CHANGES|REJECT|null"}
or sentinel: {"sentinel": "NO_RESPONSE"}
UI Options
Two UI implementations available:
- GUI (default): Dear PyGui-based, native image viewing for diagrams, read-aloud buttons
- TUI: urwid-based, text-only terminal interface, read-aloud buttons
Both UIs include:
- "Read" button on each comment for text-to-speech (requires
~/.smarttools/read-aloud/) - "Artifact" button in comment dialog for creating diagrams/visuals
Artifact Editor Integration
Both UIs integrate with the standalone Artifact Editor (~/PycharmProjects/artifact-editor) for creating visual artifacts:
GUI Integration:
- "Add Artifact" button in the comment dialog
- "New Artifact" button in the diagrams panel
- Launches artifact-editor, waits for save, captures actual file path
- Automatically adds
DIAGRAM: path/to/filemarker to comment
TUI Integration:
- "Artifact" button in the comment input widget
- If
$DISPLAYis available: launches artifact-editor GUI - If headless (SSH): falls back to
$EDITOR(nano/vim) for text editing - Diagram reference added to comment on save
Integration Protocol:
# Artifact editor outputs on save:
ARTIFACT_SAVED:/absolute/path/to/file.svg
# Parent app parses this to get actual saved path
# (handles format changes - user may switch from .puml to .svg)
# Launch GUI (default)
discussions ui
# Launch TUI
discussions ui --tui
# Or via module
python -m src.discussions.ui # GUI
python -m src.discussions.ui --tui # TUI
Keyboard Shortcuts (GUI)
| Key | Action |
|---|---|
Q |
Quit |
R |
Refresh |
T |
Run turn |
C |
Add comment |
D |
View diagrams |
Esc |
Close dialogs |
Keyboard Shortcuts (TUI)
| Key | Action |
|---|---|
q |
Quit |
r |
Run turn (invoke participants) |
d |
View diagrams (ASCII preview) |
↑/↓ |
Navigate |
Enter |
Select |
Esc |
Close dialogs |
SmartTools Arguments
Participant SmartTools accept these arguments:
--callout- Specific question or @mention context--templates-dir- Path to templates directory (default:templates)--diagrams-dir- Path to save diagrams (default:diagrams)--log-file- Path to log file for progress updates (used by TUI for parallel execution)
Source Structure
src/discussions/
├── cli.py # CLI entry point (discussions command)
├── discussion.py # Discussion model, file I/O
├── participant.py # Participant discovery from ~/.smarttools/discussion-*
├── markers.py # Marker parsing (VOTE:, Q:, TODO:, CONCERN:, etc.)
├── voting.py # Consensus calculation
├── runner.py # Turn orchestration (calls SmartTools via subprocess)
└── ui/
├── __init__.py
├── __main__.py # Module entry point
├── gui.py # Dear PyGui interface (default)
├── tui.py # urwid terminal interface
└── widgets.py # Shared UI components