Files
Agent-System/internal/workflow/runtime/orchestrator.go
T
WeiCHEN ad96f67442 refactor(workflowdef): make definition/runtime deps unidirectional
Keep registry key validation local to workflowdef with composition-root
injection, adapt FlatCatalog for projectworkflow, and drive the
orchestrator through LiveInstancePort views instead of projectinstance.
2026-09-22 21:44:57 +08:00

191 lines
4.9 KiB
Go

package runtime
import (
"context"
"fmt"
)
// TaskResolution 从冻结实例解析的运行时任务绑定。
type TaskResolution struct {
ProjectID int64
WorkflowInstID int64
StageKey string
TaskKey string
DocTaskInstID int64
ExecutorKey string
QualityGateKey string
TemplateAsset string
TemplateVersion int
AuthorUserID int64
ReviewerUserID int64
ApproverUserID int64
}
// LiveInstancePort is the runtime-owned port for reading a live instance snapshot.
// Implementations are wired at the composition root; runtime must not import projectinstance.
type LiveInstancePort interface {
Available() bool
GetLiveInstanceDetail(ctx context.Context, projectID int64) (*LiveInstanceView, *VersionView, error)
}
// LiveInstanceView is the runtime-facing projection of a live instance.
type LiveInstanceView struct {
InstanceID int64
ConfigSnapshot WorkflowPlanView
Stages []StageInstanceView
}
// WorkflowPlanView carries frozen plan duties needed for ResolveDocTask.
type WorkflowPlanView struct {
Stages []StagePlanView
}
// StagePlanView is one stage in the frozen plan.
type StagePlanView struct {
StageKey string
DocTasks []DocTaskPlanView
}
// DocTaskPlanView carries duty assignments for one doc task.
type DocTaskPlanView struct {
TaskKey string
Duties DutyView
}
// DutyView is the three-role assignment on a frozen doc task.
type DutyView struct {
AuthorUserID int64
ReviewerUserID int64
ApproverUserID int64
}
// StageInstanceView is one materialized stage in the live snapshot.
type StageInstanceView struct {
StageKey string
DocTasks []DocTaskInstanceView
}
// DocTaskInstanceView is one materialized doc task instance.
type DocTaskInstanceView struct {
ID int64
TaskKey string
}
// VersionView is the published definition projection needed for executor/template lookup.
type VersionView struct {
Definition VersionDefinitionView
}
// VersionDefinitionView holds stages/doc-task defs for lookup.
type VersionDefinitionView struct {
Stages []StageDefView
}
// StageDefView is one stage definition.
type StageDefView struct {
Key string
DocTasks []DocTaskDefView
}
// DocTaskDefView carries executor/template bindings from the published definition.
type DocTaskDefView struct {
Key string
ExecutorKey string
QualityGateKey string
TemplateAssetKey string
TemplateVersion int
Template *TemplateRefView
}
// TemplateRefView is an optional template version ref.
type TemplateRefView struct {
AssetKey string
Version int
}
// Orchestrator 只读冻结快照编排(禁止 flatten 旁路)。
type Orchestrator struct {
live LiveInstancePort
}
func NewOrchestrator(live LiveInstancePort) *Orchestrator {
return &Orchestrator{live: live}
}
// ResolveDocTask 由 doc_task_instance_id 解析 executor/模板/三权。
func (o *Orchestrator) ResolveDocTask(ctx context.Context, projectID, docTaskInstID int64) (*TaskResolution, error) {
if o == nil || o.live == nil || !o.live.Available() {
return nil, fmt.Errorf("orchestrator unavailable")
}
detail, version, err := o.live.GetLiveInstanceDetail(ctx, projectID)
if err != nil {
return nil, err
}
if detail == nil || version == nil {
return nil, fmt.Errorf("orchestrator: empty live snapshot")
}
plan := detail.ConfigSnapshot
for _, st := range detail.Stages {
for _, dt := range st.DocTasks {
if dt.ID != docTaskInstID {
continue
}
res := &TaskResolution{
ProjectID: projectID,
WorkflowInstID: detail.InstanceID,
StageKey: st.StageKey,
TaskKey: dt.TaskKey,
DocTaskInstID: dt.ID,
}
if def := lookupTaskDef(&version.Definition, st.StageKey, dt.TaskKey); def != nil {
res.ExecutorKey = def.ExecutorKey
res.QualityGateKey = def.QualityGateKey
if def.Template != nil {
res.TemplateAsset = def.Template.AssetKey
res.TemplateVersion = def.Template.Version
} else {
res.TemplateAsset = def.TemplateAssetKey
res.TemplateVersion = def.TemplateVersion
}
}
matchedDuty := false
for _, stPlan := range plan.Stages {
if stPlan.StageKey != st.StageKey {
continue
}
for _, dtPlan := range stPlan.DocTasks {
if dtPlan.TaskKey != dt.TaskKey {
continue
}
res.AuthorUserID = dtPlan.Duties.AuthorUserID
res.ReviewerUserID = dtPlan.Duties.ReviewerUserID
res.ApproverUserID = dtPlan.Duties.ApproverUserID
matchedDuty = true
}
}
if !matchedDuty {
return nil, fmt.Errorf("orchestrator: frozen plan has no duties for stage %s task %s", st.StageKey, dt.TaskKey)
}
return res, nil
}
}
return nil, fmt.Errorf("doc task instance not in live snapshot")
}
func lookupTaskDef(def *VersionDefinitionView, stageKey, taskKey string) *DocTaskDefView {
if def == nil {
return nil
}
for _, st := range def.Stages {
if st.Key != stageKey {
continue
}
for i := range st.DocTasks {
if st.DocTasks[i].Key == taskKey {
return &st.DocTasks[i]
}
}
}
return nil
}