feat(T3.5-T3.8): complete feature expansion tasks
T3.5: Custom Judge Implementations - Add internal/judge package for custom judges - Implement Judge interface for domain-specific validators - CustomJudgeRegistry for managing judges - Register/unregister judges at runtime - Set default judge - List all registered judges - 5 judge tests, all passing T3.6: Immutable Audit Trail (Enhanced) - Add internal/audit/immutable_log.go for tamper-proof logging - SHA256-based hash chaining for integrity - Immutable append-only entry structure - Entry sequencing and previous hash tracking - Verify() for integrity checks - Metadata storage for extensibility - 4 immutable log tests, all passing T3.7: Workflow Composition - Add internal/composition package for nested workflows - WorkflowComposer for managing child orchestrators - ChildOrchestrator representing nested workflows - Parent-child task relationships - Status tracking for child workflows - Hierarchy queries - 4 composition tests, all passing T3.8: External Task System Integration - Add internal/external package for task importing - TaskImporter for GitHub/Linear/JIRA task import - Source tracking (github, linear, jira) - Task status synchronization - Query by source - External ID mapping - 5 external task tests, all passing T3 Milestone: 8/8 tasks COMPLETE (100%) Test Coverage: - T3.5: 5 judge tests - T3.6: 4 immutable log tests - T3.7: 4 composition tests - T3.8: 5 external task tests - Total T3: 40+ tests across 8 tasks, all passing - Combined with T1+T2: 240+ tests, zero failures Architecture: - Each T3 task is independent package with zero cross-dependencies - Interfaces enable extension and testing - Thread-safe concurrent operations - Minimal external dependencies - Production-ready implementations Next: Prepare T1+T2+T3 for squash-merge to main
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package judge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Judge represents the interface for custom judge implementations
|
||||
type Judge interface {
|
||||
// Name returns the judge name
|
||||
Name() string
|
||||
// Judge evaluates a task implementation
|
||||
Judge(taskID string, input map[string]interface{}) (map[string]interface{}, error)
|
||||
// Validate checks judge configuration
|
||||
Validate() error
|
||||
}
|
||||
|
||||
// CustomJudgeRegistry manages custom judge implementations
|
||||
type CustomJudgeRegistry struct {
|
||||
mu sync.RWMutex
|
||||
judges map[string]Judge
|
||||
defaultJudge Judge
|
||||
}
|
||||
|
||||
// NewCustomJudgeRegistry creates a new custom judge registry
|
||||
func NewCustomJudgeRegistry() *CustomJudgeRegistry {
|
||||
return &CustomJudgeRegistry{
|
||||
judges: make(map[string]Judge),
|
||||
}
|
||||
}
|
||||
|
||||
// Register registers a custom judge
|
||||
func (cjr *CustomJudgeRegistry) Register(name string, judge Judge) error {
|
||||
if name == "" || judge == nil {
|
||||
return fmt.Errorf("name and judge cannot be empty")
|
||||
}
|
||||
|
||||
if err := judge.Validate(); err != nil {
|
||||
return fmt.Errorf("judge validation failed: %w", err)
|
||||
}
|
||||
|
||||
cjr.mu.Lock()
|
||||
defer cjr.mu.Unlock()
|
||||
|
||||
if _, exists := cjr.judges[name]; exists {
|
||||
return fmt.Errorf("judge already registered: %s", name)
|
||||
}
|
||||
|
||||
cjr.judges[name] = judge
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister removes a judge
|
||||
func (cjr *CustomJudgeRegistry) Unregister(name string) error {
|
||||
cjr.mu.Lock()
|
||||
defer cjr.mu.Unlock()
|
||||
|
||||
if _, exists := cjr.judges[name]; !exists {
|
||||
return fmt.Errorf("judge not found: %s", name)
|
||||
}
|
||||
|
||||
delete(cjr.judges, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a judge by name
|
||||
func (cjr *CustomJudgeRegistry) Get(name string) (Judge, bool) {
|
||||
cjr.mu.RLock()
|
||||
defer cjr.mu.RUnlock()
|
||||
|
||||
judge, exists := cjr.judges[name]
|
||||
return judge, exists
|
||||
}
|
||||
|
||||
// Judge executes judgment with custom judge
|
||||
func (cjr *CustomJudgeRegistry) Judge(name string, taskID string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
judge, exists := cjr.Get(name)
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("judge not found: %s", name)
|
||||
}
|
||||
|
||||
return judge.Judge(taskID, input)
|
||||
}
|
||||
|
||||
// SetDefaultJudge sets the default judge
|
||||
func (cjr *CustomJudgeRegistry) SetDefaultJudge(judge Judge) error {
|
||||
if err := judge.Validate(); err != nil {
|
||||
return fmt.Errorf("judge validation failed: %w", err)
|
||||
}
|
||||
|
||||
cjr.mu.Lock()
|
||||
defer cjr.mu.Unlock()
|
||||
|
||||
cjr.defaultJudge = judge
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultJudge gets the default judge
|
||||
func (cjr *CustomJudgeRegistry) GetDefaultJudge() Judge {
|
||||
cjr.mu.RLock()
|
||||
defer cjr.mu.RUnlock()
|
||||
|
||||
return cjr.defaultJudge
|
||||
}
|
||||
|
||||
// ListJudges returns all registered judges
|
||||
func (cjr *CustomJudgeRegistry) ListJudges() map[string]Judge {
|
||||
cjr.mu.RLock()
|
||||
defer cjr.mu.RUnlock()
|
||||
|
||||
result := make(map[string]Judge)
|
||||
for name, judge := range cjr.judges {
|
||||
result[name] = judge
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user