package main import ( "context" "errors" "strings" "time" "git.cin.su/m/Assistant/adapters/developmentexecutor" "git.cin.su/m/Assistant/contracts/work" workdomain "git.cin.su/m/Assistant/modules/work/domain" "git.cin.su/m/Assistant/modules/work/ports" ) var errDevelopmentGateCreate = errors.New("development gate must start at evidence collected with revision one") var errDevelopmentGateNotIsolated = errors.New("development gate is not isolated") var errDevelopmentVerifierNotIndependent = errors.New("development verifier is not independent") var errDevelopmentWorktreeMismatch = errors.New("verification worktree does not match gate") var errDevelopmentChecksFailed = errors.New("development verification checks failed") var errDevelopmentEvidenceRequired = errors.New("development transition evidence is required") var errDevelopmentEvidenceStage = errors.New("development transition evidence does not match stage") type developmentGateVerificationRequest struct { GateID string ExpectedRevision int64 VerifierRunID work.AgentRunID Plan developmentexecutor.Plan } type developmentGateEvidenceRequest struct { GateID string ExpectedRevision int64 NextStage work.DevelopmentStage EvidenceRef string } func recordDevelopmentGateEvidence(ctx context.Context, store ports.DevelopmentGateStore, request developmentGateEvidenceRequest, now time.Time) (work.DevelopmentGate, error) { gate, err := store.LoadDevelopmentGate(ctx, request.GateID) if err != nil { return work.DevelopmentGate{}, err } if request.ExpectedRevision < 1 || gate.Revision != request.ExpectedRevision { return gate, errDevelopmentGateNotIsolated } if strings.TrimSpace(request.EvidenceRef) == "" { return gate, errDevelopmentEvidenceRequired } switch request.NextStage { case work.DevelopmentCanary: gate.CanaryEvidence = strings.TrimSpace(request.EvidenceRef) case work.DevelopmentPromoted: gate.PromotionRef = strings.TrimSpace(request.EvidenceRef) case work.DevelopmentRolledBack: gate.RollbackRef = strings.TrimSpace(request.EvidenceRef) default: return gate, errDevelopmentEvidenceStage } advanced, err := advanceDevelopmentGate(gate, request.NextStage, now) if err != nil { return gate, err } if err := store.SaveDevelopmentGate(ctx, advanced, request.ExpectedRevision); err != nil { return gate, err } return advanced, nil } func verifyDevelopmentGate(ctx context.Context, store ports.DevelopmentGateStore, request developmentGateVerificationRequest, now time.Time, runner developmentexecutor.CommandRunner) (work.DevelopmentGate, developmentexecutor.Result, error) { gate, err := store.LoadDevelopmentGate(ctx, request.GateID) if err != nil { return work.DevelopmentGate{}, developmentexecutor.Result{}, err } if request.ExpectedRevision < 1 || gate.Revision != request.ExpectedRevision || gate.Stage != work.DevelopmentIsolated { return gate, developmentexecutor.Result{}, errDevelopmentGateNotIsolated } if request.VerifierRunID == "" || sameRun(request.VerifierRunID, gate.ProducerRunRefs) { return gate, developmentexecutor.Result{}, errDevelopmentVerifierNotIndependent } if request.Plan.WorktreeID != gate.WorktreeRef { return gate, developmentexecutor.Result{}, errDevelopmentWorktreeMismatch } result, err := developmentexecutor.Execute(ctx, request.Plan, runner) if err != nil { return gate, result, err } if !result.Passed { return gate, result, errDevelopmentChecksFailed } gate.VerifierRunRefs = append(append([]work.AgentRunID(nil), gate.VerifierRunRefs...), request.VerifierRunID) gate.VerificationEvidence = result.EvidenceRef advanced, err := advanceDevelopmentGate(gate, work.DevelopmentVerified, now) if err != nil { return gate, result, err } if err := store.SaveDevelopmentGate(ctx, advanced, request.ExpectedRevision); err != nil { return gate, result, err } return advanced, result, nil } func sameRun(value work.AgentRunID, runs []work.AgentRunID) bool { for _, run := range runs { if run == value { return true } } return false } func prepareDevelopmentGate(value work.DevelopmentGate, now time.Time) (work.DevelopmentGate, error) { if value.Stage != work.DevelopmentEvidenceCollected || value.Revision != 1 { return work.DevelopmentGate{}, errDevelopmentGateCreate } value.UpdatedAtUnixMillis = now.UnixMilli() if err := workdomain.ValidateDevelopmentGate(value); err != nil { return work.DevelopmentGate{}, err } return value, nil } func advanceDevelopmentGate(value work.DevelopmentGate, next work.DevelopmentStage, now time.Time) (work.DevelopmentGate, error) { advanced, err := workdomain.AdvanceDevelopmentGate(value, next) if err != nil { return work.DevelopmentGate{}, err } advanced.Revision = value.Revision + 1 advanced.UpdatedAtUnixMillis = now.UnixMilli() return advanced, nil }