Files
Agent-System/internal/projectinstance/workflow_scope.go
T
weichenandCursor 0912efdbd9 feat(coordinator+server): narrow document match pool to active workflow
- ActiveWorkflowScope resolves live instance task keys for the project
- chat document reader matches inside the active pool; cross-flow hits
  return ErrWrongWorkflow with switch guidance
- enrichSessionWorkflowStage injects ActiveWorkflowKey into UserCtx
- TRIGGER_TASK (coordinator + task-run API) blocks out-of-scope task keys

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-21 08:05:44 +00:00

72 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package projectinstance
import (
"context"
"chat-agent-system/internal/workflowdef"
)
// ActiveWorkflowScope 返回项目当前激活工作流的键与文档任务键集合。
//
// 语义(供意图填槽与文档正文读取收窄作用域):
// - 从未激活(无 live 实例)→ workflowKey 为空、taskKeys 为 nil → 调用方不得过滤(legacy / planning)
// - live 系实例 → workflowKey 取自 projects.active_workflow_key(回落实例快照定义键),
// taskKeys 收集全部启用文档任务,并补齐旧键/新键双向别名
// - completed / terminated 不属于激活:active_workflow_key 已在终止时清空,视为未激活
// - 存储错误原样上抛(fail-closed)
func (s *Service) ActiveWorkflowScope(ctx context.Context, projectID int64) (workflowKey string, taskKeys map[string]bool, err error) {
if s == nil || s.store == nil || projectID <= 0 {
return "", nil, nil
}
_, activeKey, _, _, err := s.store.GetProjectMeta(ctx, projectID)
if err != nil {
return "", nil, err
}
if activeKey == "" {
// 未激活:不过滤,保持现状行为。
return "", nil, nil
}
live, err := s.store.GetLiveInstance(ctx, projectID)
if err != nil {
return "", nil, err
}
if live == nil {
// 有激活键但无 live 快照:仅返回键供话术,任务集合交由调用方按空处理(不过滤)。
return activeKey, nil, nil
}
if live.ConfigSnapshot.WorkflowDefinitionKey != "" {
workflowKey = live.ConfigSnapshot.WorkflowDefinitionKey
} else {
workflowKey = activeKey
}
taskKeys = make(map[string]bool)
for _, st := range live.ConfigSnapshot.Stages {
if !st.Enabled {
continue
}
for _, dt := range st.DocTasks {
if !dt.Enabled || dt.TaskKey == "" {
continue
}
taskKeys[dt.TaskKey] = true
// 补齐旧键/新键双向别名,冻结实例与发布清单互指。
if canonical := workflowdef.CanonicalTaskKey(dt.TaskKey); canonical != dt.TaskKey {
taskKeys[canonical] = true
}
if legacy := workflowdef.ImplementationTaskKey(dt.TaskKey); legacy != dt.TaskKey {
taskKeys[legacy] = true
}
}
}
return workflowKey, taskKeys, nil
}
// TaskKeyInScope 判断任务键是否落在激活工作流任务集合内。
// taskKeys 为空(未激活 / 无快照)时一律放行,保持现状。
func TaskKeyInScope(taskKeys map[string]bool, taskKey string) bool {
if len(taskKeys) == 0 {
return true
}
return mapHasTaskKey(taskKeys, taskKey)
}