package domain import ( "errors" "slices" "git.cin.su/m/Assistant/contracts/work" ) var ErrInvalidDevelopmentGate = errors.New("invalid governed self-development gate") func AdvanceDevelopmentGate(current work.DevelopmentGate, next work.DevelopmentStage) (work.DevelopmentGate, error) { if err := ValidateDevelopmentGate(current); err != nil { return work.DevelopmentGate{}, err } if !validDevelopmentTransition(current.Stage, next) { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } current.Stage = next switch next { case work.DevelopmentProposalCreated: if current.ProposalRef == "" { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } case work.DevelopmentTaskAdmitted: if current.TaskPacketRef == "" { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } case work.DevelopmentIsolated: if current.WorktreeRef == "" { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } case work.DevelopmentVerified: if current.VerificationEvidence == "" || len(current.VerifierRunRefs) == 0 || len(current.ProducerRunRefs) == 0 || sameRuns(current.ProducerRunRefs, current.VerifierRunRefs) { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } case work.DevelopmentCanary: if current.CanaryEvidence == "" { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } case work.DevelopmentPromoted: if current.PromotionRef == "" { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } case work.DevelopmentRolledBack: if current.RollbackRef == "" { return work.DevelopmentGate{}, ErrInvalidDevelopmentGate } } return current, nil } func ValidateDevelopmentGate(value work.DevelopmentGate) error { if value.ID == "" || len(value.EvidenceRefs) == 0 || !validDevelopmentStage(value.Stage) || duplicateStrings(value.EvidenceRefs) { return ErrInvalidDevelopmentGate } return nil } func validDevelopmentTransition(current, next work.DevelopmentStage) bool { if current == work.DevelopmentCanary && next == work.DevelopmentRolledBack { return true } if current == work.DevelopmentPromoted || current == work.DevelopmentRolledBack { return false } order := []work.DevelopmentStage{work.DevelopmentEvidenceCollected, work.DevelopmentProposalCreated, work.DevelopmentTaskAdmitted, work.DevelopmentIsolated, work.DevelopmentVerified, work.DevelopmentCanary, work.DevelopmentPromoted} index := func(value work.DevelopmentStage) int { return slices.Index(order, value) } return index(next) == index(current)+1 } func validDevelopmentStage(value work.DevelopmentStage) bool { return slices.Contains([]work.DevelopmentStage{work.DevelopmentEvidenceCollected, work.DevelopmentProposalCreated, work.DevelopmentTaskAdmitted, work.DevelopmentIsolated, work.DevelopmentVerified, work.DevelopmentCanary, work.DevelopmentPromoted, work.DevelopmentRolledBack}, value) } func duplicateStrings(values []string) bool { seen := map[string]bool{} for _, value := range values { if value == "" || seen[value] { return true } seen[value] = true } return false } func sameRuns(left, right []work.AgentRunID) bool { if len(left) != len(right) { return false } for _, value := range left { if !slices.Contains(right, value) { return false } } return true }