diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 444eaa9..c101f31 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # ConfigKit v1.1.0 adoption implementation plan -Status: Stage 1 complete; Stages 2–6 are not started. +Status: Stages 1–2 complete; Stages 3–6 are not started. This plan is for a Luna coding agent implementing the selected [ConfigKit adoption target](configkit-v1.1.0.md). Follow Stage 1 through Stage 6 @@ -108,6 +108,10 @@ there is no requirement to keep this intermediate state independently usable. ## Stage 2: Align source-report vocabulary and add empty-import evidence +**Completed.** Text and YAML projections use the new vocabulary and include +ordered empty-import selections, including empty lists and nil version evidence. +The packages compile offline with Go 1.27.0; focused behavioral tests follow. + Work in `integrations/ckconfig/sources.go`. Keep its existing report preparation and delivery path; no new provider capability or operation is needed. diff --git a/integrations/ckconfig/sources.go b/integrations/ckconfig/sources.go index fa56423..df2c228 100644 --- a/integrations/ckconfig/sources.go +++ b/integrations/ckconfig/sources.go @@ -17,17 +17,24 @@ type sourceReport struct { } type sourceDTO struct { - ID string `yaml:"id"` - Label string `yaml:"label"` - Path string `yaml:"path"` - Destination string `yaml:"destination"` - Placement string `yaml:"placement"` - DestinationUnresolved bool `yaml:"destination_unresolved"` - Layer string `yaml:"layer"` - Kind string `yaml:"kind"` - UnderlayIndex int `yaml:"underlay_index"` - ImportedFrom *locationDTO `yaml:"imported_from"` - VersionCheck *sourceVersionCheckDTO `yaml:"version_check"` + ID string `yaml:"id"` + Label string `yaml:"label"` + Path string `yaml:"path"` + Destination string `yaml:"destination"` + Placement string `yaml:"placement"` + DestinationIsFallback bool `yaml:"destination_is_fallback"` + Layer string `yaml:"layer"` + Kind string `yaml:"kind"` + UnderlayIndex int `yaml:"underlay_index"` + ImportedFrom *locationDTO `yaml:"imported_from"` + VersionEvidence *sourceVersionEvidenceDTO `yaml:"version_evidence"` + EmptyImportSelections []emptyImportSelectionDTO `yaml:"empty_import_selections"` +} + +type emptyImportSelectionDTO struct { + Path string `yaml:"path"` + Location locationDTO `yaml:"location"` + Selector string `yaml:"selector"` } type locationDTO struct { @@ -36,7 +43,7 @@ type locationDTO struct { Column int `yaml:"column"` } -type sourceVersionCheckDTO struct { +type sourceVersionEvidenceDTO struct { Path string `yaml:"path"` Version versionDTO `yaml:"version"` Location *locationDTO `yaml:"location"` @@ -89,17 +96,18 @@ func runConfigSources[I, S any](invocation commandkit.Invocation, plan *configOp Path: source.Path, Destination: source.Destination.String(), Placement: placement, - DestinationUnresolved: source.DestinationIsFallback, + DestinationIsFallback: source.DestinationIsFallback, Layer: string(source.Layer), Kind: string(source.Kind), UnderlayIndex: source.UnderlayIndex, + EmptyImportSelections: make([]emptyImportSelectionDTO, 0, len(source.EmptyImportSelections)), } if source.ImportedFrom != nil { item.ImportedFrom = &locationDTO{SourceID: source.ImportedFrom.SourceID, Line: source.ImportedFrom.Line, Column: source.ImportedFrom.Column} } if source.VersionEvidence != nil { check := source.VersionEvidence - item.VersionCheck = &sourceVersionCheckDTO{ + item.VersionEvidence = &sourceVersionEvidenceDTO{ Path: check.Path.String(), Version: versionDTO{ Present: check.Version.Present, @@ -109,9 +117,12 @@ func runConfigSources[I, S any](invocation commandkit.Invocation, plan *configOp Redacted: check.Redacted, } if check.Location != nil { - item.VersionCheck.Location = &locationDTO{SourceID: check.Location.SourceID, Line: check.Location.Line, Column: check.Location.Column} + item.VersionEvidence.Location = &locationDTO{SourceID: check.Location.SourceID, Line: check.Location.Line, Column: check.Location.Column} } } + for _, selection := range source.EmptyImportSelections { + item.EmptyImportSelections = append(item.EmptyImportSelections, emptyImportSelectionDTO{Path: selection.Path.String(), Location: locationDTO{SourceID: selection.Location.SourceID, Line: selection.Location.Line, Column: selection.Location.Column}, Selector: string(selection.Selector)}) + } document.Sources = append(document.Sources, item) } if err := invocation.Context.Err(); err != nil { @@ -143,7 +154,7 @@ func writeSourceReportText(writer *bytes.Buffer, document sourceReport) { _, _ = fmt.Fprintf(writer, " path: %s\n", strconv.Quote(source.Path)) _, _ = fmt.Fprintf(writer, " destination: %s\n", strconv.Quote(source.Destination)) _, _ = fmt.Fprintf(writer, " placement: %s\n", strconv.Quote(source.Placement)) - _, _ = fmt.Fprintf(writer, " destination_unresolved: %t\n", source.DestinationUnresolved) + _, _ = fmt.Fprintf(writer, " destination_is_fallback: %t\n", source.DestinationIsFallback) _, _ = fmt.Fprintf(writer, " layer: %s\n", strconv.Quote(source.Layer)) _, _ = fmt.Fprintf(writer, " kind: %s\n", strconv.Quote(source.Kind)) _, _ = fmt.Fprintf(writer, " underlay_index: %d\n", source.UnderlayIndex) @@ -153,21 +164,29 @@ func writeSourceReportText(writer *bytes.Buffer, document sourceReport) { _, _ = fmt.Fprintf(writer, " imported_from: {source_id: %s, line: %d, column: %d}\n", strconv.Quote(source.ImportedFrom.SourceID), source.ImportedFrom.Line, source.ImportedFrom.Column) } - if source.VersionCheck == nil { - _, _ = writer.WriteString(" version_check: null\n") - continue - } - check := source.VersionCheck - _, _ = fmt.Fprintf(writer, " version_check:\n path: %s\n", strconv.Quote(check.Path)) - _, _ = fmt.Fprintf(writer, " version:\n present: %t\n null: %t\n text: %s\n", - check.Version.Present, check.Version.Null, strconv.Quote(check.Version.Text)) - if check.Location == nil { - _, _ = writer.WriteString(" location: null\n") + if source.VersionEvidence == nil { + _, _ = writer.WriteString(" version_evidence: null\n") } else { - _, _ = fmt.Fprintf(writer, " location: {source_id: %s, line: %d, column: %d}\n", - strconv.Quote(check.Location.SourceID), check.Location.Line, check.Location.Column) + check := source.VersionEvidence + _, _ = fmt.Fprintf(writer, " version_evidence:\n path: %s\n", strconv.Quote(check.Path)) + _, _ = fmt.Fprintf(writer, " version:\n present: %t\n null: %t\n text: %s\n", + check.Version.Present, check.Version.Null, strconv.Quote(check.Version.Text)) + if check.Location == nil { + _, _ = writer.WriteString(" location: null\n") + } else { + _, _ = fmt.Fprintf(writer, " location: {source_id: %s, line: %d, column: %d}\n", + strconv.Quote(check.Location.SourceID), check.Location.Line, check.Location.Column) + } + _, _ = fmt.Fprintf(writer, " redacted: %t\n", check.Redacted) + } + if len(source.EmptyImportSelections) == 0 { + _, _ = writer.WriteString(" empty_import_selections: []\n") + } else { + _, _ = writer.WriteString(" empty_import_selections:\n") + for _, selection := range source.EmptyImportSelections { + _, _ = fmt.Fprintf(writer, " - path: %s\n location: {source_id: %s, line: %d, column: %d}\n selector: %s\n", strconv.Quote(selection.Path), strconv.Quote(selection.Location.SourceID), selection.Location.Line, selection.Location.Column, strconv.Quote(selection.Selector)) + } } - _, _ = fmt.Fprintf(writer, " redacted: %t\n", check.Redacted) } }