package workflow import "fmt" type Stage string const ( StageClarification Stage = "clarification" StageFreeze Stage = "freeze" StagePlan Stage = "plan" StageReview Stage = "review" StageExecution Stage = "execution" StageVerification Stage = "verification" ) type RunStatus string const ( RunStatusRunning RunStatus = "running" RunStatusSucceeded RunStatus = "succeeded" RunStatusFailed RunStatus = "failed" RunStatusCancelled RunStatus = "cancelled" ) type LogStream string const ( LogStreamStdout LogStream = "stdout" LogStreamStderr LogStream = "stderr" LogStreamSystem LogStream = "system" ) var stageTransitions = map[Stage][]Stage{ StageClarification: {StagePlan, StageFreeze}, StageFreeze: {StageExecution}, StagePlan: {StageReview, StageExecution}, StageReview: {StageExecution}, StageExecution: {StageVerification}, StageVerification: {}, } func ValidateRunStatus(status RunStatus) error { switch status { case RunStatusRunning, RunStatusSucceeded, RunStatusFailed, RunStatusCancelled: return nil default: return fmt.Errorf("invalid run status %q", status) } } func CanTransition(from, to Stage) bool { for _, candidate := range stageTransitions[from] { if candidate == to { return true } } return false } func ValidateStage(stage Stage) error { switch stage { case StageClarification, StageFreeze, StagePlan, StageReview, StageExecution, StageVerification: return nil default: return fmt.Errorf("invalid workflow stage %q", stage) } } type Run struct { ID string `json:"id"` WorkspaceID string `json:"workspace_id"` TopicID string `json:"topic_id"` RoleName string `json:"role_name"` Stage Stage `json:"stage"` Mode string `json:"mode"` Status RunStatus `json:"status"` RequestMessageID string `json:"request_message_id,omitempty"` ConfigSnapshotJSON string `json:"config_snapshot_json"` CommandJSON string `json:"command_json"` ReplyMessageID string `json:"reply_message_id,omitempty"` ExitCode int `json:"exit_code"` StartedAt string `json:"started_at"` CompletedAt string `json:"completed_at,omitempty"` ErrorMessage string `json:"error_message,omitempty"` } func (r Run) Validate() error { if r.WorkspaceID == "" { return fmt.Errorf("workspace id is required") } if r.TopicID == "" { return fmt.Errorf("topic id is required") } if r.RoleName == "" { return fmt.Errorf("role name is required") } if err := ValidateStage(r.Stage); err != nil { return err } if err := ValidateRunStatus(r.Status); err != nil { return err } return nil } type RunLog struct { RunID string `json:"run_id"` Seq int `json:"seq"` Stream LogStream `json:"stream"` Content string `json:"content"` CreatedAt string `json:"created_at"` } func ValidateLogStream(stream LogStream) error { switch stream { case LogStreamStdout, LogStreamStderr, LogStreamSystem: return nil default: return fmt.Errorf("invalid log stream %q", stream) } } func (l RunLog) Validate() error { if l.RunID == "" { return fmt.Errorf("run id is required") } if err := ValidateLogStream(l.Stream); err != nil { return err } if l.Content == "" { return fmt.Errorf("log content is required") } if l.Seq < 0 { return fmt.Errorf("log seq must be >= 0") } return nil }