claw-code/.guardrails/mcp-server/internal/models/task_attempt.go

82 lines
2.6 KiB
Go

package models
import (
"fmt"
"time"
"github.com/google/uuid"
)
// TaskAttempt represents a record of a task attempt for three strikes tracking
type TaskAttempt struct {
ID uuid.UUID `json:"id" db:"id"`
SessionID string `json:"session_id" db:"session_id"`
TaskID *string `json:"task_id,omitempty" db:"task_id"`
AttemptNumber int `json:"attempt_number" db:"attempt_number"`
AttemptedAt time.Time `json:"attempted_at" db:"attempted_at"`
ErrorMessage string `json:"error_message" db:"error_message"`
ErrorCategory string `json:"error_category" db:"error_category"`
Resolution string `json:"resolution" db:"resolution"`
ResolvedAt *time.Time `json:"resolved_at,omitempty" db:"resolved_at"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// ErrorCategory represents the category of an error
type ErrorCategory string
const (
ErrorCategorySyntax ErrorCategory = "syntax"
ErrorCategoryRuntime ErrorCategory = "runtime"
ErrorCategoryLogic ErrorCategory = "logic"
ErrorCategoryTimeout ErrorCategory = "timeout"
ErrorCategoryOther ErrorCategory = "other"
)
// ValidErrorCategories contains all valid error categories
var ValidErrorCategories = []string{
string(ErrorCategorySyntax),
string(ErrorCategoryRuntime),
string(ErrorCategoryLogic),
string(ErrorCategoryTimeout),
string(ErrorCategoryOther),
}
// IsValidErrorCategory checks if an error category is valid
func IsValidErrorCategory(category string) bool {
for _, c := range ValidErrorCategories {
if c == category {
return true
}
}
return false
}
// Validate checks if the task attempt is valid for creation
func (t *TaskAttempt) Validate() error {
if t.SessionID == "" {
return fmt.Errorf("session_id is required")
}
if len(t.SessionID) > 100 {
return fmt.Errorf("session_id must be at most 100 characters")
}
if t.TaskID != nil && len(*t.TaskID) > 100 {
return fmt.Errorf("task_id must be at most 100 characters")
}
if t.AttemptNumber <= 0 {
return fmt.Errorf("attempt_number must be positive")
}
if t.ErrorCategory != "" && !IsValidErrorCategory(t.ErrorCategory) {
return fmt.Errorf("invalid error_category: %s", t.ErrorCategory)
}
if t.Resolution == "" {
return fmt.Errorf("resolution is required")
}
if !IsValidResolutionStatus(t.Resolution) {
return fmt.Errorf("invalid resolution: %s", t.Resolution)
}
if t.ResolvedAt != nil && t.Resolution != string(ResolutionResolved) && t.Resolution != string(ResolutionEscalated) && t.Resolution != string(ResolutionAbandoned) {
return fmt.Errorf("resolved_at can only be set when resolution is 'resolved', 'escalated', or 'abandoned'")
}
return nil
}