Published manifests now use bd_/pm_/tm_/sw_ prefixes so workflows cannot share a doc id. Frozen instances keep the old keys; lookup, templates, and duty checks treat the two names as the same task. Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
3.4 KiB
Go
99 lines
3.4 KiB
Go
package workflowdef
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// legacyToCanonical 是 D12 的旧任务键 → 新任务键。
|
|
// 冻结实例继续持有旧键;新发布的清单使用新键。两边必须指向同一套步骤和模板。
|
|
var legacyToCanonical = map[string]string{
|
|
"intake": "bd_intake",
|
|
"research": "bd_research",
|
|
"feasibility_dev": "bd_feasibility_dev",
|
|
"feasibility_reg": "bd_feasibility_reg",
|
|
"user_requirements": "bd_user_requirements",
|
|
"prelim_design": "bd_prelim_design",
|
|
"cdo_quote": "bd_cdo_quote",
|
|
"system_reqs": "pm_system_reqs",
|
|
"test_manager": "tm_system_verification",
|
|
"software_charter": "sw_software_charter",
|
|
"source_inventory": "sw_source_inventory",
|
|
"current_state_audit": "sw_current_state_audit",
|
|
"architecture_baseline": "sw_architecture_baseline",
|
|
"software_risk_register": "sw_software_risk_register",
|
|
"product_requirements": "sw_product_requirements",
|
|
"scope_backlog": "sw_scope_backlog",
|
|
"target_architecture": "sw_target_architecture",
|
|
"decision_log": "sw_decision_log",
|
|
"bootstrap_roadmap": "sw_bootstrap_roadmap",
|
|
"verification_strategy": "sw_verification_strategy",
|
|
"operations_baseline": "sw_operations_baseline",
|
|
"bootstrap_readiness": "sw_bootstrap_readiness",
|
|
"change_brief": "sw_change_brief",
|
|
"implementation_plan": "sw_implementation_plan",
|
|
"change_manifest": "sw_change_manifest",
|
|
"adr_delta": "sw_adr_delta",
|
|
"test_report": "sw_test_report",
|
|
"security_review": "sw_security_review",
|
|
"release_plan": "sw_release_plan",
|
|
"release_notes": "sw_release_notes",
|
|
"retrospective": "sw_retrospective",
|
|
"backlog_delta": "sw_backlog_delta",
|
|
}
|
|
|
|
var canonicalToLegacy map[string]string
|
|
|
|
func init() {
|
|
canonicalToLegacy = make(map[string]string, len(legacyToCanonical))
|
|
for legacy, canonical := range legacyToCanonical {
|
|
canonicalToLegacy[canonical] = legacy
|
|
}
|
|
}
|
|
|
|
// CanonicalTaskKey 把旧键收成发布用的新键。未知键(含 rd_*)原样返回。
|
|
func CanonicalTaskKey(key string) string {
|
|
key = strings.TrimSpace(key)
|
|
if canonical, ok := legacyToCanonical[key]; ok {
|
|
return canonical
|
|
}
|
|
return key
|
|
}
|
|
|
|
// ImplementationTaskKey 把新键收成现有步骤开关仍使用的旧键。
|
|
// 存量执行路径不改写;新键只是别名。
|
|
func ImplementationTaskKey(key string) string {
|
|
key = strings.TrimSpace(key)
|
|
if legacy, ok := canonicalToLegacy[key]; ok {
|
|
return legacy
|
|
}
|
|
return key
|
|
}
|
|
|
|
// SameTaskKey 判断两个键是否同一任务(旧键与新键视为相同)。
|
|
func SameTaskKey(a, b string) bool {
|
|
ca := CanonicalTaskKey(a)
|
|
cb := CanonicalTaskKey(b)
|
|
return ca != "" && ca == cb
|
|
}
|
|
|
|
var namespacedDocID = regexp.MustCompile(`^(doc_proj\d+_)(.+)$`)
|
|
|
|
// AlternateDocIDs 给出同一文档的另一种键名形式。
|
|
// 账本不批量迁移:旧 ID 仍可查,新 ID 也要能指回旧行。
|
|
func AlternateDocIDs(docID string) []string {
|
|
m := namespacedDocID.FindStringSubmatch(strings.TrimSpace(docID))
|
|
if len(m) != 3 {
|
|
return nil
|
|
}
|
|
prefix, suffix := m[1], m[2]
|
|
var out []string
|
|
if canonical := CanonicalTaskKey(suffix); canonical != suffix {
|
|
out = append(out, prefix+canonical)
|
|
}
|
|
if legacy := ImplementationTaskKey(suffix); legacy != suffix {
|
|
out = append(out, prefix+legacy)
|
|
}
|
|
return out
|
|
}
|