package config import ( "bytes" "encoding/json" "fmt" "sort" ) // effectivePipelineProjection is the single safe, schema-specific view used // for inspection, comparison, and identity. Runtime metadata and unresolved // family declarations have no slot in this type. type effectivePipelineProjection struct { Workspace WorkspaceConfig `yaml:"workspace" json:"workspace"` Campaigns CampaignsConfig `yaml:"campaigns" json:"campaigns"` Storage StorageConfig `yaml:"storage" json:"storage"` Spool SpoolConfig `yaml:"spool" json:"spool"` Cache CacheConfig `yaml:"cache" json:"cache"` Publish *PublishConfig `yaml:"publish" json:"publish"` Secrets *SecretsConfig `yaml:"secrets" json:"secrets"` WhisperX WhisperXConfig `yaml:"whisperx" json:"whisperx"` Seriatim SeriatimConfig `yaml:"seriatim" json:"seriatim"` Audita AuditaConfig `yaml:"audita" json:"audita"` Normalize *NormalizeConfig `yaml:"normalize" json:"normalize"` Trim *TrimConfig `yaml:"trim" json:"trim"` Render *RenderConfig `yaml:"render" json:"render"` Scriptorium *effectiveScriptoriumConfig `yaml:"scriptorium" json:"scriptorium"` Notarius *NotariusConfig `yaml:"notarius" json:"notarius"` Notification NotificationConfig `yaml:"notification" json:"notification"` } type effectiveScriptoriumConfig struct { Binary string `yaml:"binary" json:"binary"` ConfigPath string `yaml:"config_path" json:"config_path"` Timeout string `yaml:"timeout" json:"timeout"` RenderDebug bool `yaml:"render_debug" json:"render_debug"` Artifacts map[string]ScriptoriumArtifactConfig `yaml:"artifacts" json:"artifacts"` } func projectEffectivePipeline(cfg *PipelineConfig, logicalNotarius bool) effectivePipelineProjection { projection := effectivePipelineProjection{ Workspace: cfg.Workspace, Campaigns: cfg.Campaigns, Storage: cfg.Storage, Spool: cfg.Spool, Cache: cfg.Cache, Publish: cfg.Publish, Secrets: cfg.Secrets, WhisperX: cfg.WhisperX, Seriatim: cfg.Seriatim, Audita: cfg.Audita, Normalize: cfg.Normalize, Trim: cfg.Trim, Render: cfg.Render, Notarius: cfg.Notarius, Notification: cfg.Notification, } if cfg.Scriptorium != nil { projection.Scriptorium = &effectiveScriptoriumConfig{ Binary: cfg.Scriptorium.Binary, ConfigPath: cfg.Scriptorium.ConfigPath, Timeout: cfg.Scriptorium.Timeout, RenderDebug: cfg.Scriptorium.RenderDebug, Artifacts: cfg.Scriptorium.Artifacts, } } if logicalNotarius && cfg.Notarius != nil && cfg.resolution != nil && cfg.resolution.logicalNotariusCaptured { notarius := *cfg.Notarius notarius.ConfigPath = cfg.resolution.logicalNotariusConfig notarius.WorkingDirectory = cfg.resolution.logicalNotariusWorking projection.Notarius = ¬arius } return projection } func projectionJSON(projection effectivePipelineProjection) ([]byte, error) { data, err := json.Marshal(projection) if err != nil { return nil, fmt.Errorf("encode effective pipeline projection: %w", err) } return data, nil } func projectionValueRecords(projection effectivePipelineProjection) ([]EffectivePipelineValueRecord, error) { data, err := projectionJSON(projection) if err != nil { return nil, err } decoder := json.NewDecoder(bytes.NewReader(data)) decoder.UseNumber() var root map[string]any if err := decoder.Decode(&root); err != nil { return nil, fmt.Errorf("decode effective pipeline projection: %w", err) } var records []EffectivePipelineValueRecord appendProjectionRecords(&records, "", root) sort.Slice(records, func(i, j int) bool { return records[i].Path < records[j].Path }) return records, nil } func appendProjectionRecords(records *[]EffectivePipelineValueRecord, path string, value any) { if mapping, ok := value.(map[string]any); ok && len(mapping) > 0 { keys := make([]string, 0, len(mapping)) for key := range mapping { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { childPath := key if path != "" { childPath = path + "." + key } appendProjectionRecords(records, childPath, mapping[key]) } return } encoded, _ := json.Marshal(value) *records = append(*records, EffectivePipelineValueRecord{Path: path, Value: string(encoded)}) }