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.history if model == nil { return Explanation{}, false } selected := model.selectHistory(path) if len(selected) == 0 && !model.hasObservedShape(path) { return Explanation{}, false } selections := model.indexReplaySelections(selected) explanation := Explanation{Path: path.clone(), Effective: valueSnapshotWithLookup(r.final, path, r.secrets, model.documentLookup)} if info, ok := r.metadata.Lookup(path); ok { explanation.Origin = cloneOrigin(info.Origin) } working := document.Clone(model.baseline) if baseline, found := selections.byPayload[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 := selections.application(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 := selections.byPayload[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 := selections.application(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 := selections.byPayload[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 := selections.resolutionGroup(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 } // historyReplaySelections is query-local dispatch state, not another evidence // or attribution owner. Replay still follows the model's ordered payloads. type historyReplaySelections struct { byPayload map[historyPayloadRef]historySelection applicationFallbacks map[historyPayloadRef]historySelection } func (model *historyModel) indexReplaySelections(ordered []historySelection) historyReplaySelections { indexed := historyReplaySelections{ byPayload: make(map[historyPayloadRef]historySelection, len(ordered)), applicationFallbacks: make(map[historyPayloadRef]historySelection), } for _, selection := range ordered { ref := selection.payload indexed.byPayload[ref] = selection if selection.role != historyRoleOperation || ref.substep != 0 { continue } var payloads []historySourcePayload switch ref.phase { case historyPhaseSources: payloads = model.sourcePayloads case historyPhaseCLI: payloads = model.cliPayloads default: continue } if ref.id < 0 || ref.id >= len(payloads) { continue } payload := payloads[ref.id] if payload.kind == historySourceOverlayPayload && payload.hasApplicationRef { indexed.applicationFallbacks[payload.applicationRef] = selection } } return indexed } func (selections historyReplaySelections) application(ref historyPayloadRef) (historySelection, bool) { if direct, found := selections.byPayload[ref]; found { return direct, true } // Only removed/replaced paths lacking a direct contribution need fallback. selection, found := selections.applicationFallbacks[ref] return selection, 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 (selections historyReplaySelections) resolutionGroup(group resolutionReplayGroup) (historySelection, bool) { var fallback historySelection found := false for _, recordID := range group.originalRecordIDs { selection, ok := selections.byPayload[historyPayloadRef{phase: historyPhaseResolution, id: recordID}] if !ok { continue } if recordID == group.coveringRecordID { return selection, true } if !found { fallback, found = selection, true } } return fallback, found }