Date: November 22, 2025 Status: Accepted Deciders: Chief Architect, Lead Developer Classification: Architectural (Core Pattern) Issue: #332 (DOCUMENTATION-STORED-PROCS)
The question “Are there stored procedures in use?” requires clarification because Piper Morgan implements a “stored procedures” pattern, but at the application layer rather than the database layer.
Traditionally, stored procedures live in the database (SQL, PL/pgSQL) to encapsulate multi-step business logic. This approach has trade-offs:
Database-layer Stored Procedures:
Piper Morgan implements the stored procedure concept in the application layer using async Python, routing through the OrchestrationEngine. This decouples multi-step workflows from the database, while maintaining the core benefit: composable, reusable, versioned business logic.
During #356 (PERF-INDEX) and #532 (PERF-CONVERSATION-ANALYTICS) work, complex multi-step procedures were needed to:
These procedures are composed, executed, and tested entirely in Python—making them version-controlled, testable, and database-agnostic.
Piper Morgan uses application-layer stored procedures through orchestrated Python workflows rather than database-layer SQL procedures.
Intent → WorkflowFactory → Workflow (sequence of tasks) → OrchestrationEngine → Result
Executes workflows as composable, multi-step procedures:
class OrchestrationEngine:
async def execute_workflow(self, workflow: Workflow) -> WorkflowResult:
"""Execute a complete workflow with task dependencies and error recovery"""
for task in workflow.tasks:
task_result = await self._execute_task(task, workflow)
# Critical task failure stops execution
if task_result.failed and task.critical:
return WorkflowResult(status=FAILED, error=...)
return WorkflowResult(status=COMPLETED, ...)
Key Methods:
execute_workflow() - Multi-step orchestrated executioncreate_workflow_from_intent() - Intent → Workflow translation_execute_task() - Individual task executionhandle_query_intent() - Query-specific orchestrationPattern:
Defines and instantiates workflows from intents:
class WorkflowFactory:
def __init__(self):
self.workflow_registry = {
"create_github_issue": WorkflowType.CREATE_TICKET,
"analyze_data": WorkflowType.ANALYZE_FILE,
"generate_report": WorkflowType.GENERATE_REPORT,
# ... mappings for each intent type
}
self.validation_registry = {
WorkflowType.CREATE_TICKET: {
"context_requirements": {
"critical": ["original_message"],
"important": ["project_id"],
"optional": ["labels", "priority"],
},
"performance_threshold_ms": 50,
"pre_execution_checks": ["project_resolution"],
},
# ... validation rules per workflow
}
async def create_from_intent(self, intent: Intent) -> Workflow:
"""Create a workflow from an intent"""
# Validate context before execution
self._validate_workflow_context(intent)
# Return appropriate workflow type
workflow_type = self.workflow_registry[intent.type]
return Workflow(type=workflow_type, tasks=[...])
Key Capabilities:
Pattern:
Routes intents to appropriate handlers, each implementing multi-step procedures:
class IntentService:
async def process_intent(self, intent: Intent) -> IntentProcessingResult:
"""Route intent to appropriate handler"""
# Intent → Handler dispatch
if intent.category == IntentCategory.QUERY:
return await self._handle_query_intent(intent)
elif intent.category == IntentCategory.EXECUTION:
return await self._handle_execution_intent(intent)
elif intent.category == IntentCategory.ANALYSIS:
return await self._handle_analysis_intent(intent)
# ... 25+ intent types with dedicated handlers
async def _handle_execution_intent(self, intent: Intent) -> IntentProcessingResult:
"""Multi-step procedure: validate → prepare → execute"""
# Step 1: Validate
validation = await self._validate_execution(intent)
if not validation.valid:
return IntentProcessingResult(status=FAILED, error=...)
# Step 2: Prepare
workflow = await self.factory.create_from_intent(intent)
# Step 3: Execute through OrchestrationEngine
result = await self.orchestration_engine.execute_workflow(workflow)
return IntentProcessingResult(status=SUCCEEDED, data=result)
Key Methods:
Handler Categories:
_handle_query_intent(), _handle_standup_query()_handle_execution_intent(), _handle_create_issue()_handle_analysis_intent(), _handle_analyze_data()_handle_strategic_planning(), _handle_prioritization()_handle_learn_pattern(), _handle_learning_intent()| Aspect | Application Layer (Piper) | Database Layer (SQL) |
|---|---|---|
| Technology | Python async/await | SQL/PL/pgSQL |
| Location | services/orchestration/ | Database procedures |
| Composability | High (Python functions) | Medium (SQL procedures) |
| Version Control | Git commits | Database migrations |
| Testing | Unit tests in Python | Database/integration tests |
| Debugging | Python debugger tools | Database query logs |
| Database Agnostic | Yes (any DB backend) | No (DB-specific) |
| Network Traffic | More round-trips | Fewer round-trips |
| Deployment | Code deploy | Code + migration deploy |
| Error Handling | Python exceptions | SQL error codes |
| Integration | Tight with app logic | Separate layer |
| Performance | Depends on impl | Optimized by DB |
A workflow is a sequence of typed tasks with dependencies:
@dataclass
class Task:
id: str
type: TaskType # ANALYZE_REQUEST, EXTRACT_REQUIREMENTS, etc.
params: Dict[str, Any]
critical: bool = False # Stop workflow if fails
@dataclass
class Workflow:
id: str
type: WorkflowType
status: WorkflowStatus
tasks: List[Task]
# Example workflow for intent processing:
workflow = Workflow(
type=WorkflowType.CREATE_TICKET,
tasks=[
Task(type=ANALYZE_REQUEST, params={"intent": intent}),
Task(type=EXTRACT_REQUIREMENTS, params={"analyzed": ...}, critical=True),
Task(type=IDENTIFY_DEPENDENCIES, params={"requirements": ...}),
Task(type=EXECUTE_GITHUB_ACTION, params={"dependencies": ...}),
]
)
1. Intent received
↓
2. WorkflowFactory.create_from_intent() → Workflow
├─ Validate context requirements
├─ Check pre-execution conditions
└─ Build task sequence
↓
3. OrchestrationEngine.execute_workflow()
├─ For each task in workflow.tasks:
│ ├─ _execute_task()
│ ├─ Track task status
│ └─ If critical and failed → abort
├─ Track timing and metrics
└─ Return WorkflowResult
↓
4. Return result to caller
# Critical task failure = workflow failure
if task.critical and task_result.failed:
return WorkflowResult(
status=FAILED,
error=f"Critical task {task.id} failed"
)
# Non-critical task failure = continue with next task
# (allows partial success in complex procedures)
When to prefer application-layer procedures:
When database procedures might be better:
Piper’s Choice: Application layer, because:
Supports:
Supports Implementation Of:
Differs From:
services/orchestration/engine.py:63-490services/orchestration/workflow_factory.py:22-539services/intent/intent_service.py:65-5184services/domain/models.py (Intent, Task, Workflow classes)services/shared_types.py (TaskStatus, WorkflowStatus, TaskType)# Input: User asks "Show me my projects"
intent = Intent(
type="query",
content="Show me my projects",
category=IntentCategory.QUERY
)
# IntentService dispatch
result = await intent_service.process_intent(intent)
→ calls _handle_query_intent()
# Handler creates workflow
workflow = await workflow_factory.create_from_intent(intent)
→ type: WorkflowType.LIST_PROJECTS
→ tasks: [validate, fetch, format, return]
# Orchestration engine executes
result = await orchestration_engine.execute_workflow(workflow)
→ executes each task in order
→ returns list of projects
# Input: "Create a GitHub issue about performance"
intent = Intent(
type="execution",
content="Create GitHub issue about performance",
category=IntentCategory.EXECUTION
)
# Multi-step workflow created by factory
workflow = Workflow(
type=WorkflowType.CREATE_TICKET,
tasks=[
Task(ANALYZE_REQUEST, critical=True), # Understand intent
Task(EXTRACT_REQUIREMENTS, critical=True), # What's needed
Task(IDENTIFY_DEPENDENCIES, critical=False), # Dependencies
Task(EXECUTE_GITHUB_ACTION, critical=False), # Create issue
]
)
# Orchestration executes all steps
result = await engine.execute_workflow(workflow)
→ Returns GitHub issue creation result
→ If ANALYZE_REQUEST fails → abort (critical)
→ If IDENTIFY_DEPENDENCIES fails → continue (non-critical)
Test individual handlers and workflow composition:
async def test_create_from_intent():
factory = WorkflowFactory()
intent = Intent(type="create_github_issue", ...)
workflow = await factory.create_from_intent(intent)
assert workflow.type == WorkflowType.CREATE_TICKET
assert len(workflow.tasks) > 0
assert workflow.tasks[0].type == TaskType.ANALYZE_REQUEST
Test full workflow execution:
async def test_execute_github_creation_workflow():
engine = OrchestrationEngine()
workflow = Workflow(
type=WorkflowType.CREATE_TICKET,
tasks=[...],
)
result = await engine.execute_workflow(workflow)
assert result.status == WorkflowStatus.COMPLETED
assert result.github_issue_created
Verify workflow execution performance:
# Each workflow type has performance_threshold_ms
CREATE_TICKET: 50ms
LIST_PROJECTS: 30ms
ANALYZE_FILE: 75ms
Question: “Are there stored procedures in use?”
Answer: Yes—at the application layer.
Piper Morgan implements a stored procedures pattern through orchestrated Python workflows rather than database-layer SQL procedures. This decision enables:
This is a deliberate architectural choice, not an accident or limitation.
Decision Made: November 22, 2025 Status: Accepted Implementation: Active (OrchestrationEngine, WorkflowFactory, IntentService) Documentation: Issue #332 (DOCUMENTATION-STORED-PROCS)
🤖 Generated with Claude Code