package workbench import "fmt" var legacyMetadataKeys = []string{"legacy_import", "legacy_history", "legacy_activation"} type configurationSaveKey struct{} type legacyImportProtection struct { ReadOnly bool `json:"read_only"` RequiresReview bool `json:"requires_review"` NeedsConfiguration bool `json:"needs_configuration"` MissingConfiguration []string `json:"missing_configuration"` } // Import provenance and historical output are migration-owned, opaque data. // Typed map/task updates must retain them without treating them as executable state. func resourceData(r *Resource, value any) JSON { data := asJSON(value) for _, key := range legacyMetadataKeys { if original, exists := r.Data[key]; exists { data[key] = original } } return data } func validateLegacyMetadataUpdate(original, proposed JSON) error { for _, key := range legacyMetadataKeys { before, existed := original[key] after, supplied := proposed[key] if supplied && (!existed || compact(before) != compact(after)) { return fmt.Errorf("%w: 历史迁移标记和原始记录不可修改", ErrInvalid) } } return nil } func requireLegacyExecutable(r *Resource) error { value, exists := r.Data["legacy_import"] if !exists { return nil } // Invalid protection metadata must fail closed rather than enable old jobs. if value == nil { return fmt.Errorf("%w: 历史迁移标记无效,请检查迁移结果", ErrInvalid) } decoded, err := decode[struct { Flags *legacyImportProtection `json:"legacy_import"` }](JSON{"legacy_import": value}) if err != nil || decoded.Flags == nil { return fmt.Errorf("%w: 历史迁移标记无效,请检查迁移结果", ErrInvalid) } flags := decoded.Flags if activation, ok := r.Data["legacy_activation"]; ok { state, err := decode[struct { Activation struct { Status string `json:"status"` } `json:"legacy_activation"` }](JSON{"legacy_activation": activation}) if err == nil && state.Activation.Status == "active" { return nil } } if flags.ReadOnly { return fmt.Errorf("%w: 此记录为只读历史资料,原正文、质检和成稿已保留;请编辑配置并保存后继续创作", ErrInvalid) } if flags.RequiresReview { return fmt.Errorf("%w: 此历史资源尚未完成配置复核,请编辑配置并保存后执行", ErrInvalid) } if flags.NeedsConfiguration || len(flags.MissingConfiguration) > 0 { return fmt.Errorf("%w: 此历史资源缺少可执行配置,请先完成迁移配置核对", ErrInvalid) } return nil } func requireExecutable(r *Resource) error { if err := requireLegacyExecutable(r); err != nil { return err } if r.Status == "needs_configuration" { return fmt.Errorf("%w: 资源缺少可执行配置,请重新选择并保存可用的导图、日期、画布和 Agent;历史正文、质检和成稿仍保留", ErrInvalid) } return nil } func requireActionExecutable(r *Resource, action string) error { if r.Kind == "content-tasks" && taskActionUsesOnlySavedContent(action) { return requireLegacyExecutable(r) } return requireExecutable(r) }