Audience: Developers, architects, and technical contributors Last Updated: November 17, 2025 Status: Active technical documentation
Adding docs? Know the two surfaces. The pmorgan.tech site builds only the visitor-facing subset of
docs/— the working corpus (internal/,briefs/,briefing/,operations/, omnibus logs, and similar) is deliberately excluded viadocs/_config.ymland lives on GitHub only (scoped 2026-08-12, CIO-ratified; rationale indocs/internal/operations/docs-site-scoping-proposal-2026-08-12.md). If you add a new top-level directory underdocs/, decide which surface it belongs to and update the_config.ymlexclude list accordingly — an unclassified directory ships to the public site by default. The exclude list is owned by Docs.
# Clone repository
git clone https://github.com/mediajunkie/piper-morgan-product.git
cd piper-morgan-product
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Start services (Docker)
docker compose up -d
# Initialize database
python scripts/init-db.py
# Start application
python main.py
Access Points:
main.py)web/app.py)services/)domain/models.py (source of truth)intent/intent_service.pyuser_context/user_context_service.pyintegrations/ (7+ plugins)services/)templates/)web/static/)main.py # Application entry point
web/app.py # FastAPI application
services/domain/models.py # Domain models (source of truth)
services/shared_types.py # Shared types and enums
services/config.py # Configuration settings
services/intent/intent_service.py # Intent classification
services/user_context/ # User context service
services/integrations/ # Plugin integrations
templates/ # Jinja2 templates
web/static/ # Static assets
config/PIPER.user.md # User configuration
# Check system health
python main.py status
# Run tests
python -m pytest tests/unit/ -v
python -m pytest tests/integration/ -v
# Start with debugging
python main.py --debug
services/intent/handlers/_handle_your_intent method in IntentServicetests/manual/services/integrations/demo/get_config(), validate(), execute()services/integrations/your_plugin/# Connect to PostgreSQL (port 5433)
docker exec -it piper-postgres psql -U piper -d piper_morgan
# View migrations
python scripts/show-migrations.py
# Run migrations
python scripts/run-migrations.py
# Reset database (development only!)
python scripts/reset-db.py
templates/web/static/css/web/static/js/| Integration | Type | Status | Purpose |
|---|---|---|---|
| GitHub | API | Active | Issue tracking, PR analysis |
| Slack | Webhook | Active | Notifications, messaging |
| Calendar | API | Active | Schedule, availability |
| Notion | API | Active | Document management |
| Demo | Example | Reference | Template for new plugins |
| MCP | Protocol | Active | Model context protocol |
| Spatial | Custom | Active | Specialized features |
All plugins implement BaseIntegration:
class YourPlugin(BaseIntegration):
"""Plugin description"""
@property
def name(self) -> str:
return "your_plugin"
@property
def version(self) -> str:
return "1.0.0"
async def get_config(self) -> Dict:
"""Return configuration schema"""
pass
async def validate(self) -> ValidationResult:
"""Validate integration setup"""
pass
async def execute(self, command: str, **kwargs) -> ExecutionResult:
"""Execute integration command"""
pass
Intent → Classification → Handler Selection → Plugin Execution
Source of Truth: services/domain/models.py
All models are defined here. Don’t scatter definitions across services.
Location: services/shared_types.py
# All enums defined here
class IntentCategory(Enum):
IDENTITY = "identity"
TEMPORAL = "temporal"
STATUS = "status"
# ... etc
File: config/PIPER.user.md (not YAML)
# PIPER User Configuration
## GitHub
- repository: mediajunkie/piper-morgan-product
- token: ${GITHUB_TOKEN}
## Slack
- workspace_token: ${SLACK_TOKEN}
- user_id: U12345
## Calendar
- provider: google
- credentials: ${CALENDAR_CREDS}
# Required
OPENAI_API_KEY=sk-...
GITHUB_TOKEN=ghp_...
# Optional
ANTHROPIC_API_KEY=sk-ant-...
SLACK_BOT_TOKEN=xoxb-...
CALENDAR_CREDENTIALS=...
# Internal
DATABASE_URL=postgresql://piper:password@localhost:5433/piper_morgan
REDIS_URL=redis://localhost:6379
Location: services/config.py
class Settings:
"""Application settings from environment"""
debug: bool = os.getenv("DEBUG", "false").lower() == "true"
# ... other settings
tests/
├── unit/ # Fast, isolated tests
├── integration/ # Service integration tests
└── manual/ # Interactive debugging scripts
# All unit tests
python -m pytest tests/unit/ -v
# Specific test file
python -m pytest tests/unit/test_intent_service.py -v
# With coverage
python -m pytest tests/unit/ --cov=services
# Integration tests (slower, require services)
python -m pytest tests/integration/ -v
# Manual debugging
python tests/manual/manual_notion_test.py
test_*.py or *_test.py in tests/unit/ or tests/integration/manual_*.py in tests/manual/ (can use hardcoded IDs, load_dotenv())Error: could not connect to server: Connection refused
Solution:
# Check if Docker containers are running
docker ps | grep postgres
# Start services
docker-compose up -d
# Verify port 5433 (not 5432)
netstat -an | grep 5433
GET /static/css/style.css → HTTP 404
Solution:
web/static/web/app.py: app.mount("/static", StaticFiles(...))TemplateNotFound: skip-link.html
Solution:
ls templates/skip-link.htmltemplates/ (parent of web/)web/app.py:
templates = Jinja2Templates(
directory=os.path.join(os.path.dirname(__file__), "..", "templates")
)
Solution:
tests/unit/test_intent_classifier.pyPluginNotFoundError: your_plugin
Solution:
BaseIntegrationpython -c "from services.integrations.your_plugin import YourPlugin"from services.logger import logger
# In your code
logger.debug(f"Value: {variable}")
logger.info(f"Operation: {description}")
logger.error(f"Error: {error_message}")
import pdb
pdb.set_trace() # Execution pauses here
# Database state
docker exec -it piper-postgres psql -U piper -d piper_morgan -c "SELECT * FROM users LIMIT 5;"
# Redis cache
docker exec -it piper-redis redis-cli
# API responses
curl -H "Content-Type: application/json" \
-d '{"message":"what is my status?"}' \
http://localhost:8001/api/v1/intent
# Benchmark intent processing
python scripts/benchmark-intent.py
# Profile database queries
python -m pytest tests/unit/test_intent_service.py -v --durations=10
services/config.py).env or PIPER.user.mdfrom pydantic import BaseModel, validator
class IntentRequest(BaseModel):
message: str
@validator('message')
def message_not_empty(cls, v):
if not v.strip():
raise ValueError('Message cannot be empty')
return v
# Install production dependencies
pip install -r requirements.txt --no-dev
# Build Docker images
docker build -t piper-morgan:latest .
# Run migrations
python scripts/run-migrations.py
# Start application
python main.py
/api/health endpoint for system statusNeed Help? Check the main README for support options or consult the NAVIGATION guide to find relevant documentation.