package domain import ( "fmt" "strings" "time" ) // EventPlanPhaseVerified records that Orchestra ran a plan phase's automated // verification and what happened. The implementer never emits it: an agent may // request verification, and only the plane can establish it. const EventPlanPhaseVerified = "PlanPhaseVerified" // PlanPhaseStatus is what Orchestra established about one phase. type PlanPhaseStatus string const ( // PlanPhaseInProgress is the default and the outcome of a failed run. It // is never written by a request, only left in place by one. PlanPhaseInProgress PlanPhaseStatus = "in_progress" // PlanPhaseAwaitingManual means every automated check passed and manual // steps remain. A human signs those off; the agent cannot. PlanPhaseAwaitingManual PlanPhaseStatus = "awaiting_manual_verification" // PlanPhaseVerified means nothing further is required for this phase. PlanPhaseVerified PlanPhaseStatus = "verified" ) func (s PlanPhaseStatus) Valid() bool { switch s { case PlanPhaseInProgress, PlanPhaseAwaitingManual, PlanPhaseVerified: return true } return false } // PlanPhaseRequestStatus is the single value an implementer may write. Every // other status is a conclusion Orchestra reaches, so allowing an agent to // assert one would let it declare its own work verified. const PlanPhaseRequestStatus = "ready_for_verification" // PlanPhaseRecord is one verification run, bound to the plan it belongs to and // the commit it ran against. // // Both bindings are load-bearing. Without PlanRef, a phase verified under plan // A survives into plan B, which is the same class of bug as a review that // outlives the commit it examined. Without AtSHA, "verified" outlives the code // that made it true. type PlanPhaseRecord struct { PlanRef string `json:"plan_ref"` PhaseID string `json:"phase_id"` Status PlanPhaseStatus `json:"status"` Commands [][]string `json:"commands,omitempty"` ExitCodes []int `json:"exit_codes,omitempty"` AtSHA string `json:"at_sha"` // EvidenceRef is the CAS ref of the captured command output. EvidenceRef string `json:"evidence_ref,omitempty"` At time.Time `json:"at"` // ManualAtSHA is the tree a human was actually looking at when they signed // this phase off. A manual check on most projects is a human reading // output, so a sign-off establishes something about one tree and nothing // about the next one (F63). Rerunning the automated half re-establishes it // at the new commit; the manual half has to be given again, and this is // what makes the difference visible instead of assumed. ManualAtSHA string `json:"manual_at_sha,omitempty"` } // Stale reports whether the tree has moved since this phase was verified. A // stale record is retained and labelled rather than discarded: it is still // true that the phase passed at that commit, and hiding it would lose the // provenance. What it must never do is read as current. func (r PlanPhaseRecord) Stale(headSHA string) bool { return headSHA != "" && r.AtSHA != "" && r.AtSHA != headSHA } // PlanProgress is the durable verification state for one accepted plan. type PlanProgress struct { // PlanRef is the plan these records belong to. A record from a superseded // plan is never counted, so a replan cannot inherit progress it did not // earn. PlanRef string `json:"plan_ref"` Phases []PlanPhaseRecord `json:"phases,omitempty"` } // PlanPhases returns the records that belong to the currently accepted plan. // A task whose plan was superseded reports none, whatever the log still holds. func (t Task) PlanPhases() []PlanPhaseRecord { if t.PlanProgress == nil || t.PlanRef == "" || t.PlanProgress.PlanRef != t.PlanRef { return nil } return t.PlanProgress.Phases } // PlanPhase returns the record for one phase of the accepted plan. func (t Task) PlanPhase(id string) (PlanPhaseRecord, bool) { for _, r := range t.PlanPhases() { if r.PhaseID == id { return r, true } } return PlanPhaseRecord{}, false } // PlanPhaseSubject is the decision subject a manual sign-off carries. The key // binds the approval to one phase of one plan, so a later "looks good" on an // unrelated thread cannot satisfy a gate nobody was talking about. func PlanPhaseSubject(planRef, phaseID string) string { return "plan_phase_verification:" + planRef + ":" + phaseID } const maxVerificationCommands = 16 func ValidatePlanPhaseVerified(p map[string]any) error { planRef, _ := p["plan_ref"].(string) if strings.TrimSpace(planRef) == "" { return fmt.Errorf("%w: plan_ref required", ErrInvalid) } phaseID, _ := p["phase_id"].(string) if strings.TrimSpace(phaseID) == "" { return fmt.Errorf("%w: phase_id required", ErrInvalid) } status, _ := p["status"].(string) if !PlanPhaseStatus(status).Valid() { return fmt.Errorf("%w: status %q is not a plan phase status", ErrInvalid, status) } if sha, _ := p["at_sha"].(string); len(sha) != 40 { return fmt.Errorf("%w: at_sha must be a full commit sha", ErrInvalid) } codes, _ := p["exit_codes"].([]any) if len(codes) > maxVerificationCommands { return fmt.Errorf("%w: %d exit codes exceeds the %d command bound", ErrInvalid, len(codes), maxVerificationCommands) } // A verified phase whose commands failed would be a contradiction the // reducer could not detect later. if PlanPhaseStatus(status) != PlanPhaseInProgress { for _, c := range codes { if code, ok := c.(float64); !ok || code != 0 { return fmt.Errorf("%w: status %q cannot carry a non-zero exit code", ErrInvalid, status) } } } return nil }