package engine import "gitea.maximumdirect.net/eric/ConfigKit/internal/document" // ValueSnapshot is a detached, deterministic YAML representation of one value. // Present distinguishes absence or deletion from an explicit null value. type ValueSnapshot struct { Present bool Redacted bool YAML string } // ValueChange describes one explicit definition or audited domain operation. type ValueChange struct { Before ValueSnapshot After ValueSnapshot Origin Origin Reasons []string Dependencies []Path } // SecretReferenceInfo describes a successfully resolved effective reference. // Name is the environment-variable name, never its value. type SecretReferenceInfo struct { Name string } // Explanation is a detached safe account of one effective or historical path. type Explanation struct { Path Path Effective ValueSnapshot Origin Origin History []ValueChange SecretReference *SecretReferenceInfo } // Explain returns safe effective and chronological values for a known final or // historical path. Unknown and malformed paths return false. func (r *Inspection) Explain(path Path) (Explanation, bool) { if r == nil || r.final == nil || !path.valid() { return Explanation{}, false } model := r.metadata.historyModel if model == nil { return Explanation{}, false } selections := model.selectHistory(path) if len(selections) == 0 && !model.hasObservedShape(path) { return Explanation{}, false } explanation := Explanation{Path: path.clone(), Effective: valueSnapshotWithLookup(r.final, path, r.secrets, r.immutableLookup)} if info, ok := r.metadata.Lookup(path); ok { explanation.Origin = cloneOrigin(info.Origin) } working := document.Clone(model.baseline) if baseline, found := historySelectionForPayload(selections, historyPayloadRef{phase: historyPhaseBaseline, id: 0}); found && baseline.after { explanation.History = append(explanation.History, ValueChange{ After: valueSnapshot(working, path, r.secrets), Origin: cloneOrigin(baseline.attribution.origin), }) } replayIndex := new(mappingIndex) for payloadID, payload := range model.sourcePayloads { if payload.kind != historySourceApplicationPayload { continue } application := payload.application before := valueSnapshot(working, path, r.secrets) applyHistoryApplication(&working, application, replayIndex) selected, relevant := historySelectionForApplication(model, selections, historyPhaseSources, historyPayloadRef{phase: historyPhaseSources, id: payloadID}) if !relevant { continue } after := valueSnapshot(working, path, r.secrets) if !before.Present && !after.Present { continue } explanation.History = append(explanation.History, ValueChange{ Before: before, After: after, Origin: cloneOrigin(selected.attribution.origin), }) } appendInheritanceExplanation(&explanation, &working, path, model, selections, r.secrets) // Intermediate input replacements do not permanently suppress defaults: // decoding applies them to the final input tree. Show that reconciliation // explicitly before typed CLI contributions and domain resolution. if checkpoint := model.preCLICheckpoint; checkpoint != nil { selection, selected := historySelectionForPayload(selections, historyPayloadRef{phase: historyPhasePreCLI, id: 0}) replayed, _ := documentAtPath(working, path) decoded, _ := documentAtPathWithLookup(checkpoint.root, path, model.documentLookup) if selected && !configurationValuesEqual(replayed, decoded) { origin, reasons := checkpointExplanationAttribution(selection) explanation.History = append(explanation.History, ValueChange{ Before: valueSnapshot(working, path, r.secrets), After: valueSnapshotWithLookup(checkpoint.root, path, r.secrets, model.documentLookup), Origin: origin, Reasons: reasons, }) } working = document.Clone(checkpoint.root) } for payloadID, payload := range model.cliPayloads { if payload.kind != historySourceApplicationPayload { continue } applicationRef := historyPayloadRef{phase: historyPhaseCLI, id: payloadID} before := valueSnapshot(working, path, r.secrets) applyHistoryApplication(&working, payload.application, replayIndex) selected, relevant := historySelectionForApplication(model, selections, historyPhaseCLI, applicationRef) if !relevant { continue } after := valueSnapshot(working, path, r.secrets) if !before.Present && !after.Present { continue } explanation.History = append(explanation.History, ValueChange{ Before: before, After: after, Origin: cloneOrigin(selected.attribution.origin), }) } if checkpoint := model.postCLICheckpoint; checkpoint != nil { selection, selected := historySelectionForPayload(selections, historyPayloadRef{phase: historyPhasePostCLI, id: 0}) replayed, _ := documentAtPath(working, path) effectiveInput, _ := documentAtPathWithLookup(checkpoint.root, path, model.documentLookup) if selected && !configurationValuesEqual(replayed, effectiveInput) { origin, reasons := checkpointExplanationAttribution(selection) explanation.History = append(explanation.History, ValueChange{ Before: valueSnapshot(working, path, r.secrets), After: valueSnapshotWithLookup(checkpoint.root, path, r.secrets, model.documentLookup), Origin: origin, Reasons: reasons, }) } working = document.Clone(checkpoint.root) } if model.resolutionPayload != nil { for _, group := range model.resolutionPayload.replayGroups { selection, selected := historySelectionForResolutionGroup(selections, group) if !selected || group.coveringRecordID < 0 || group.coveringRecordID >= len(model.resolutionPayload.snapshots) { continue } covering := model.resolutionPayload.snapshots[group.coveringRecordID] before := valueSnapshot(working, path, r.secrets) replaceHistoryPath(&working, group.change.Path, covering.after) after := valueSnapshot(working, path, r.secrets) if !before.Present && !after.Present { continue } explanation.History = append(explanation.History, ValueChange{ Before: before, After: after, Origin: cloneOrigin(selection.attribution.origin), Reasons: append([]string(nil), group.change.Reasons...), Dependencies: clonePaths(group.change.Dependencies), }) } } if !explanation.Effective.Present && len(explanation.History) == 0 { return Explanation{}, false } if name, ok := r.secretReferences[path.String()]; ok && !pathIsSecret(r.secrets, path) { explanation.SecretReference = &SecretReferenceInfo{Name: name} } return explanation, true } func historySelectionForPayload(selections []historySelection, ref historyPayloadRef) (historySelection, bool) { for _, selection := range selections { if selection.payload == ref { return selection, true } } return historySelection{}, false } func historySelectionForApplication(model *historyModel, selections []historySelection, phase historyPhase, applicationRef historyPayloadRef) (historySelection, bool) { var selected historySelection found := false if direct, ok := historySelectionForPayload(selections, applicationRef); ok { selected, found = direct, true } var payloads []historySourcePayload switch phase { case historyPhaseSources: payloads = model.sourcePayloads case historyPhaseCLI: payloads = model.cliPayloads default: return selected, found } for payloadID, candidate := range payloads { if candidate.kind != historySourceOverlayPayload || !candidate.hasApplicationRef || candidate.applicationRef != applicationRef { continue } if operation, ok := historySelectionForPayload(selections, historyPayloadRef{phase: phase, id: payloadID}); ok { selected, found = operation, true } } return selected, found } func checkpointExplanationAttribution(selection historySelection) (Origin, []string) { if selection.hasAttribution { return cloneOrigin(selection.attribution.origin), append([]string(nil), selection.attribution.reasons...) } return Origin{Operation: ChangeDecoding}, nil } func historySelectionForResolutionGroup(selections []historySelection, group resolutionReplayGroup) (historySelection, bool) { var fallback historySelection found := false for _, recordID := range group.originalRecordIDs { selection, ok := historySelectionForPayload(selections, historyPayloadRef{phase: historyPhaseResolution, id: recordID}) if !ok { continue } if recordID == group.coveringRecordID { return selection, true } if !found { fallback, found = selection, true } } return fallback, found }