Files
Test cb94314bcc 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
2026-08-23 17:48:15 -07:00

118 lines
2.7 KiB
Go

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
}