b5c37f693b
F63, found live on run 19. A manual check on these projects is a human reading what the code prints. RecordPlanPhaseVerification asked only whether a sign-off for that plan and phase existed, and one exists forever, so rerunning a phase's automated checks at a new commit carried the human half along with it. The rig proved it twice: two operator commits and two re-verification requests, each coming back verified without anyone looking. The reducer now records which tree the human confirmed, the record carries it forward as provenance, and a run whose commit does not match it waits for the human again. A sign-off given before any run has no confirmed tree and still counts, so the ordinary ordering is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
143 lines
5.5 KiB
Go
143 lines
5.5 KiB
Go
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
|
|
}
|