98 lines
3.2 KiB
Go
98 lines
3.2 KiB
Go
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
|
|
}
|