227 lines
8.3 KiB
Markdown
227 lines
8.3 KiB
Markdown
# Poimen Declarative Workflow Implementation Plan
|
|||
|
|
|
||
|
|
## Executive Summary
|
||
|
|
|
||
|
|
**Current State**: Hardcoded workflows (OrchestratorWorkflow, TaskUnitWorkflow)
|
||
|
|
- Fixed flow: Clone → Plan → Implement → Judge → Commit
|
||
|
|
- New flows require recompilation
|
||
|
|
- No customer-specified sequencing
|
||
|
|
|
||
|
|
**Target State**: API-driven dynamic workflows
|
||
|
|
- Customer sends JSON specifying activity sequence
|
||
|
|
- Routing engine executes dynamically
|
||
|
|
- Support 3 patterns: Sequential, Await-Task-Complete, Retry
|
||
|
|
- Backward compatible with existing cmd/starter
|
||
|
|
|
||
|
|
**Timeline**: 4 weeks | **Effort**: ~80 story points | **Team**: 2-3 engineers
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 1: Core Routing Engine (Week 1) — 16 hours
|
||
|
|
|
||
|
|
**Goal**: Build engine that parses WorkflowSpec and executes activity sequences dynamically
|
||
|
|
|
||
|
|
**Files to Create**:
|
||
|
|
- `internal/routing/types.go` — Data structures for WorkflowSpec, ActivityStep, RetryConfig
|
||
|
|
- `internal/routing/jsonpath_evaluator.go` — Parameter interpolation using JSONPath
|
||
|
|
- `internal/routing/execution_context.go` — Execution state management
|
||
|
|
- `internal/routing/activity_registry.go` — Activity name → implementation mapper
|
||
|
|
- `statemachine/routing_workflow.go` — **Core RoutingWorkflow that ties everything together**
|
||
|
|
- Tests: `*_test.go` files for each module
|
||
|
|
|
||
|
|
**Key Implementation Details**:
|
||
|
|
- RoutingWorkflow is the main Temporal workflow
|
||
|
|
- It parses activity steps and executes them sequentially
|
||
|
|
- Uses JSONPath to interpolate parameters: `${step_1.output.path}`
|
||
|
|
- Supports retry logic, error catching, and conditional jumps
|
||
|
|
- All activities are pre-registered in worker pool
|
||
|
|
|
||
|
|
**Backward Compatibility**: ✅ Keep OrchestratorWorkflow, TaskUnitWorkflow unchanged
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 2: API Server (Week 2) — 10 hours
|
||
|
|
|
||
|
|
**Goal**: HTTP API that accepts WorkflowSpec JSON and manages workflow execution
|
||
|
|
|
||
|
|
**Files to Create**:
|
||
|
|
- `cmd/api-server/main.go` — Server bootstrap, Temporal client connection
|
||
|
|
- `cmd/api-server/handlers.go` — POST /api/v1/workflows, GET /api/v1/workflows/{id}/status
|
||
|
|
- `cmd/api-server/validation.go` — WorkflowSpec validation (references, timeouts, etc.)
|
||
|
|
- `cmd/api-server/server.go` — Server config and lifecycle
|
||
|
|
- Tests: handlers, validation, integration tests
|
||
|
|
|
||
|
|
**Endpoints**:
|
||
|
|
```
|
||
|
|
POST /api/v1/workflows
|
||
|
|
→ Submit WorkflowSpec
|
||
|
|
→ Returns: {workflow_id, status, polling_url}
|
||
|
|
|
||
|
|
GET /api/v1/workflows/{workflow_id}/status
|
||
|
|
→ Poll execution status
|
||
|
|
→ Returns: {status, steps_completed, results, final_output}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Key Details**:
|
||
|
|
- Validates WorkflowSpec before submission
|
||
|
|
- Submits to RoutingWorkflow (not OrchestratorWorkflow)
|
||
|
|
- Caches workflow metadata locally
|
||
|
|
- Polls Temporal for status
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 3: Homelab Frontend Integration (Week 3) — 5 hours
|
||
|
|
|
||
|
|
**Goal**: Expose API through ingress, connect to gateway
|
||
|
|
|
||
|
|
**Files to Create**:
|
||
|
|
- `k8s/api-server-deployment.yaml` — K8s Deployment, 2 replicas
|
||
|
|
- `k8s/api-server-service.yaml` — K8s Service for api-server
|
||
|
|
- `k8s/api-server-networkpolicy.yaml` — Allow ingress traffic, deny by default
|
||
|
|
- `homelab-frontend/k8s/gateway-routes.yaml` — Route /api/v1/workflows to api-server
|
||
|
|
|
||
|
|
**Key Details**:
|
||
|
|
- api-server connects to Temporal: `temporal-frontend.temporal:7233`
|
||
|
|
- Ingress routes external traffic to api-server:8080
|
||
|
|
- NetworkPolicy allows only ingress-nginx → api-server traffic
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Phase 4: Migration & Docs (Week 4) — 7 hours
|
||
|
|
|
||
|
|
**Files to Create**:
|
||
|
|
- `docs/API.md` — API reference (endpoints, request/response examples)
|
||
|
|
- `docs/MIGRATION.md` — How to migrate from cmd/starter to API
|
||
|
|
- `docs/examples/sequential-workflow.json` — Example: Clone → Analyze → Implement → Judge
|
||
|
|
- `docs/examples/retry-workflow.json` — Example: with retry + error handling
|
||
|
|
- `docs/examples/conditional-workflow.json` — Example: with catch blocks
|
||
|
|
|
||
|
|
**Key Details**:
|
||
|
|
- Comprehensive examples for all 3 patterns
|
||
|
|
- cmd/starter continues to work (backward compatible)
|
||
|
|
- Documentation emphasizes JSONPath parameter passing
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Three Composite Patterns
|
||
|
|
|
||
|
|
### Pattern A: Sequential (Request-Response)
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"activities": [
|
||
|
|
{"name": "Step1", "resource": "ActivityA", "parameters": {...}, "next": "Step2"},
|
||
|
|
{"name": "Step2", "resource": "ActivityB", "parameters": {"input": "${Step1.output}"}, "next": "Step3"},
|
||
|
|
{"name": "Step3", "resource": "ActivityC", "parameters": {...}, "end": true}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
**Flow**: Step1 → Step2 (receives Step1's output) → Step3
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Pattern B: Await-Task-Complete (Callback)
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"activities": [
|
||
|
|
{"name": "LaunchJob", "type": "await-callback", "resource": "LaunchActivity", "heartbeat_timeout": "5m", "next": "ProcessResult"},
|
||
|
|
{"name": "ProcessResult", "resource": "ProcessActivity", "parameters": {"result": "${LaunchJob.callback}"}, "end": true}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
**Flow**: LaunchJob (waits for callback) → ProcessResult (receives callback data)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Pattern C: Retry with Error Handling
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"activities": [
|
||
|
|
{"name": "Generate", "resource": "ImplementerActivity", "retry": {"maxAttempts": 3, "backoffRate": 2.0}, "catch": [{"errorEquals": ["Timeout"], "next": "HandleTimeout"}], "next": "Verify"},
|
||
|
|
{"name": "Verify", "resource": "JudgeActivity", "end": true},
|
||
|
|
{"name": "HandleTimeout", "type": "pass", "result": {"status": "timeout"}, "end": true}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
**Flow**: Generate (retry 3x) → if success: Verify, else: HandleTimeout
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Success Criteria
|
||
|
|
|
||
|
|
- ✅ RoutingWorkflow executes dynamic sequences correctly
|
||
|
|
- ✅ JSONPath parameter passing works (nested references, arrays)
|
||
|
|
- ✅ Error handling and catch blocks work
|
||
|
|
- ✅ Retry logic respects backoff intervals
|
||
|
|
- ✅ API validates WorkflowSpec (no invalid refs)
|
||
|
|
- ✅ Status polling returns step-by-step results
|
||
|
|
- ✅ All tests pass (>90% coverage)
|
||
|
|
- ✅ Backward compatible (cmd/starter still works)
|
||
|
|
- ✅ API exposed via ingress at `/api/v1/workflows`
|
||
|
|
- ✅ Performance: <200ms submit, <100ms poll
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Architecture Diagram
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─ Customer/Frontend ─────────────────────────┐
|
||
|
|
│ POST /api/v1/workflows (JSON WorkflowSpec) │
|
||
|
|
└────────────────────┬────────────────────────┘
|
||
|
|
↓
|
||
|
|
┌──────────────────────┐
|
||
|
|
│ API Server │
|
||
|
|
│ • Validate spec │
|
||
|
|
│ • Submit to Temporal │
|
||
|
|
│ • Cache metadata │
|
||
|
|
└────────────┬─────────┘
|
||
|
|
↓
|
||
|
|
┌────────────────────────────────┐
|
||
|
|
│ Temporal Server │
|
||
|
|
│ RoutingWorkflow │
|
||
|
|
│ ├─ Parse activities │
|
||
|
|
│ ├─ Evaluate JSONPath │
|
||
|
|
│ ├─ Execute sequence │
|
||
|
|
│ └─ Handle errors/retries │
|
||
|
|
└────────────┬───────────────────┘
|
||
|
|
↓
|
||
|
|
┌────────────────────────────────┐
|
||
|
|
│ Temporal Worker Pool │
|
||
|
|
│ • Poll queue │
|
||
|
|
│ • Execute activities │
|
||
|
|
│ • Return results │
|
||
|
|
└────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Risk Mitigation
|
||
|
|
|
||
|
|
| Risk | Mitigation |
|
||
|
|
|------|-----------|
|
||
|
|
| JSONPath bugs | Unit tests + property-based testing |
|
||
|
|
| Activity registry mismatch | Validate registered activities match spec |
|
||
|
|
| Temporal network issues | Retry logic + timeouts in API server |
|
||
|
|
| Cache staleness | Event-driven cache invalidation |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Rollback Plan
|
||
|
|
|
||
|
|
| Phase | Rollback Strategy |
|
||
|
|
|-------|------------------|
|
||
|
|
| Phase 1 | Keep old workflows, don't use RoutingWorkflow |
|
||
|
|
| Phase 2 | Disable api-server, fall back to cmd/starter |
|
||
|
|
| Phase 3 | Remove ingress, access api-server directly |
|
||
|
|
| Phase 4 | Continue supporting both APIs (cmd/starter + API) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Next Steps
|
||
|
|
|
||
|
|
1. **Week 1**: Start Phase 1 implementation (RoutingWorkflow)
|
||
|
|
2. **Week 2**: Start Phase 2 (API Server)
|
||
|
|
3. **Week 3**: Deploy to K8s (Phase 3)
|
||
|
|
4. **Week 4**: Document, test, release (Phase 4)
|
||
|
|
5. **Post-Release**: Monitor, gather feedback, plan Phase 2 enhancements (parallel, conditional branching)
|
||
|
|
|