Finalize structured diagnostic aggregation

This commit is contained in:
2026-08-27 16:40:17 +00:00
parent 480680b257
commit 4dbbf68051
112 changed files with 656 additions and 893 deletions

View File

@@ -30,45 +30,45 @@ type ReferenceMaterializationOptions struct {
WorkingDir string
}
func MaterializeReferences(resolved ResolvedPipeline, catalog ModuleCatalog, options ReferenceMaterializationOptions) (ResolvedPipeline, []contracts.Warning, error) {
func MaterializeReferences(resolved ResolvedPipeline, catalog ModuleCatalog, options ReferenceMaterializationOptions) (ResolvedPipeline, []contracts.ProducerDiagnostic, error) {
out := resolved
out.ChunkReferences = CloneReferenceTarget(resolved.ChunkReferences)
chunkReferenceSet, chunkWarnings, err := materializeReferenceTarget(resolved.ID, resolved.ChunkReferences, "", catalog, options)
chunkReferenceSet, chunkDiagnostics, err := materializeReferenceTarget(resolved.ID, resolved.ChunkReferences, "", catalog, options)
if err != nil {
return ResolvedPipeline{}, nil, err
}
out.ChunkReferences.ReferenceSet = chunkReferenceSet
warnings := append([]contracts.Warning(nil), chunkWarnings...)
diagnostics := contracts.CloneProducerDiagnostics(chunkDiagnostics)
if len(resolved.Steps) == 0 {
return out, warnings, nil
return out, diagnostics, nil
}
materializeLane := func(lane ResolvedArtifactLane) (ResolvedArtifactLane, []contracts.Warning, error) {
materializeLane := func(lane ResolvedArtifactLane) (ResolvedArtifactLane, []contracts.ProducerDiagnostic, error) {
materializedLane := lane
var allWarnings []contracts.Warning
var allDiagnostics []contracts.ProducerDiagnostic
materializedLane.ExtractReferences = CloneReferenceTarget(lane.ExtractReferences)
materializedLane.MergeReferences = CloneReferenceTarget(lane.MergeReferences)
materializedLane.NormalizeReferences = CloneReferenceTarget(lane.NormalizeReferences)
extractReferenceSet, laneWarnings, err := materializeReferenceTarget(resolved.ID, lane.ExtractReferences, lane.ArtifactKind, catalog, options)
extractReferenceSet, laneDiagnostics, err := materializeReferenceTarget(resolved.ID, lane.ExtractReferences, lane.ArtifactKind, catalog, options)
if err != nil {
return ResolvedArtifactLane{}, nil, err
}
materializedLane.ExtractReferences.ReferenceSet = extractReferenceSet
allWarnings = append(allWarnings, laneWarnings...)
allDiagnostics = append(allDiagnostics, laneDiagnostics...)
mergeReferenceSet, laneWarnings, err := materializeReferenceTarget(resolved.ID, lane.MergeReferences, lane.ArtifactKind, catalog, options)
mergeReferenceSet, laneDiagnostics, err := materializeReferenceTarget(resolved.ID, lane.MergeReferences, lane.ArtifactKind, catalog, options)
if err != nil {
return ResolvedArtifactLane{}, nil, err
}
materializedLane.MergeReferences.ReferenceSet = mergeReferenceSet
allWarnings = append(allWarnings, laneWarnings...)
allDiagnostics = append(allDiagnostics, laneDiagnostics...)
normalizeReferenceSet, laneWarnings, err := materializeReferenceTarget(resolved.ID, lane.NormalizeReferences, lane.ArtifactKind, catalog, options)
normalizeReferenceSet, laneDiagnostics, err := materializeReferenceTarget(resolved.ID, lane.NormalizeReferences, lane.ArtifactKind, catalog, options)
if err != nil {
return ResolvedArtifactLane{}, nil, err
}
materializedLane.NormalizeReferences.ReferenceSet = normalizeReferenceSet
allWarnings = append(allWarnings, laneWarnings...)
return materializedLane, allWarnings, nil
allDiagnostics = append(allDiagnostics, laneDiagnostics...)
return materializedLane, allDiagnostics, nil
}
if len(resolved.Steps) > 0 {
out.Steps = make([]ResolvedPipelineStep, len(resolved.Steps))
@@ -76,36 +76,16 @@ func MaterializeReferences(resolved ResolvedPipeline, catalog ModuleCatalog, opt
out.Steps[i].ID = step.ID
out.Steps[i].ArtifactLanes = make([]ResolvedArtifactLane, len(step.ArtifactLanes))
for j, lane := range step.ArtifactLanes {
materializedLane, laneWarnings, err := materializeLane(lane)
materializedLane, laneDiagnostics, err := materializeLane(lane)
if err != nil {
return ResolvedPipeline{}, nil, err
}
out.Steps[i].ArtifactLanes[j] = materializedLane
warnings = append(warnings, laneWarnings...)
diagnostics = append(diagnostics, laneDiagnostics...)
}
}
}
return out, warnings, nil
}
// ReferenceDiagnostics converts framework-owned empty-reference findings into
// structured process diagnostics. Legacy callers may continue to use the
// materializer's warning return while the framework migration is in progress.
func ReferenceDiagnostics(warnings []contracts.Warning) []contracts.ProducerDiagnostic {
diagnostics := make([]contracts.ProducerDiagnostic, 0, len(warnings))
for _, warning := range warnings {
if warning.ReasonCode != "empty_reference" {
continue
}
diagnostics = append(diagnostics, contracts.ProducerDiagnostic{
Disposition: contracts.DiagnosticDispositionWarning,
Category: contracts.DiagnosticCategoryConfiguration,
ReasonCode: "empty_reference",
OccurrenceCount: 1,
Samples: []contracts.DiagnosticSample{{Scope: warning.Scope, Message: warning.Message}},
})
}
return contracts.CloneProducerDiagnostics(diagnostics)
return out, diagnostics, nil
}
func materializeReferenceTarget(
@@ -114,7 +94,7 @@ func materializeReferenceTarget(
artifactKind contracts.ArtifactKind,
catalog ModuleCatalog,
options ReferenceMaterializationOptions,
) (contracts.ReferenceSet, []contracts.Warning, error) {
) (contracts.ReferenceSet, []contracts.ProducerDiagnostic, error) {
if len(target.Bindings) == 0 {
return contracts.ReferenceSet{}, nil, nil
}
@@ -129,7 +109,7 @@ func materializeReferenceTarget(
}
set := contracts.ReferenceSet{Slots: make(map[string]contracts.ResolvedReferenceSlot, len(target.Bindings))}
var warnings []contracts.Warning
var diagnostics []contracts.ProducerDiagnostic
for _, binding := range target.Bindings {
slotName := strings.TrimSpace(binding.SlotName)
slot, ok := slotByName[slotName]
@@ -179,11 +159,7 @@ func materializeReferenceTarget(
return contracts.ReferenceSet{}, nil, fmt.Errorf("%s reference slot %q path %q media type %q is not accepted", referenceTargetContext(pipelineID, target), slotName, path, mediaType)
}
if len(content) == 0 {
warnings = append(warnings, contracts.Warning{
Scope: referenceWarningScope(pipelineID, target, slotName),
ReasonCode: "empty_reference",
Message: fmt.Sprintf("reference slot %q for %s is bound to an empty file", slotName, referenceTargetLabel(target)),
})
diagnostics = append(diagnostics, contracts.ProducerDiagnostic{Disposition: contracts.DiagnosticDispositionWarning, Category: contracts.DiagnosticCategoryConfiguration, ReasonCode: "empty_reference", OccurrenceCount: 1, Samples: []contracts.DiagnosticSample{{Scope: referenceWarningScope(pipelineID, target, slotName), Message: fmt.Sprintf("reference slot %q for %s is bound to an empty file", slotName, referenceTargetLabel(target))}}})
}
item := contracts.ReferenceItem{
@@ -200,7 +176,7 @@ func materializeReferenceTarget(
Items: []contracts.ReferenceItem{item},
}
}
return set, warnings, nil
return set, diagnostics, nil
}
type referenceSizeLimitError struct {