104 lines
4.3 KiB
Go
104 lines
4.3 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/evidencecontext"
|
|
)
|
|
|
|
// debugEvidenceContextSummary intentionally contains only publication-safe
|
|
// identifiers and aggregate counts. The evidence document itself can include
|
|
// source text and must never be written to this debug envelope.
|
|
type debugEvidenceContextSummary struct {
|
|
ArtifactKind contracts.ArtifactKind `json:"artifact_kind"`
|
|
MediaType string `json:"media_type"`
|
|
SchemaID string `json:"schema_id"`
|
|
SchemaName string `json:"schema_name"`
|
|
SchemaVersion string `json:"schema_version"`
|
|
SelectedLanes []string `json:"selected_lanes"`
|
|
WindowUnits int `json:"window_units"`
|
|
UnitCount int `json:"unit_count"`
|
|
SourceDigest string `json:"source_digest"`
|
|
}
|
|
|
|
// buildOutputEvidenceContext projects the prepared output policy from accepted
|
|
// normalized artifacts. It is intentionally separate from lane execution so
|
|
// checkpointed normalized outputs use the same reconstruction path.
|
|
func buildOutputEvidenceContext(prepared *PreparedPipeline, doc *source.SourceDocument, outputs []contracts.SerializedOutput) (*contracts.SerializedArtifact, *debugEvidenceContextSummary, error) {
|
|
if prepared == nil || prepared.evidencePlan == nil {
|
|
return nil, nil, nil
|
|
}
|
|
if doc == nil {
|
|
return nil, nil, fmt.Errorf("evidence context output: source document is unavailable")
|
|
}
|
|
if prepared.artifactCodecs == nil {
|
|
return nil, nil, fmt.Errorf("evidence context output: artifact codecs are unavailable")
|
|
}
|
|
|
|
byLane := make(map[string]contracts.SerializedOutput, len(outputs))
|
|
for _, output := range outputs {
|
|
laneID := strings.TrimSpace(output.LaneID)
|
|
if _, exists := byLane[laneID]; exists {
|
|
return nil, nil, fmt.Errorf("evidence context output: accepted normalized outputs contain duplicate lane %q", laneID)
|
|
}
|
|
byLane[laneID] = contracts.CloneSerializedOutput(output)
|
|
}
|
|
|
|
request := evidencecontext.BuildRequest{
|
|
Source: doc,
|
|
WindowUnits: prepared.evidencePlan.policy.WindowUnits,
|
|
SourceRefs: make([]source.SourceRef, 0),
|
|
}
|
|
for _, lane := range prepared.evidencePlan.lanes {
|
|
output, ok := byLane[lane.laneID]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if output.SourceID != doc.ID {
|
|
return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized output source is incompatible", lane.laneID)
|
|
}
|
|
if output.Artifact.Kind != lane.kind {
|
|
return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized output artifact kind is incompatible", lane.laneID)
|
|
}
|
|
value, err := prepared.artifactCodecs.Decode(contracts.CloneSerializedArtifact(output.Artifact))
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized artifact cannot be decoded", lane.laneID)
|
|
}
|
|
references, err := lane.project(value)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("evidence context output lane %q: accepted normalized artifact cannot be projected", lane.laneID)
|
|
}
|
|
request.SourceRefs = append(request.SourceRefs, references...)
|
|
}
|
|
|
|
document, err := evidencecontext.Build(request)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("evidence context output: accepted evidence references are invalid")
|
|
}
|
|
content, err := evidencecontext.New().Encode(document)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("evidence context output: evidence context serialization failed")
|
|
}
|
|
artifact := &contracts.SerializedArtifact{
|
|
Kind: evidencecontext.ArtifactKind,
|
|
Schema: evidencecontext.New().Schema(),
|
|
MediaType: evidencecontext.MediaType,
|
|
Content: content,
|
|
}
|
|
summary := debugEvidenceContextSummary{
|
|
ArtifactKind: artifact.Kind,
|
|
MediaType: artifact.MediaType,
|
|
SchemaID: artifact.Schema.ID,
|
|
SchemaName: artifact.Schema.Name,
|
|
SchemaVersion: artifact.Schema.Version,
|
|
SelectedLanes: append([]string(nil), prepared.evidencePlan.policy.LaneIDs...),
|
|
WindowUnits: prepared.evidencePlan.policy.WindowUnits,
|
|
SourceDigest: doc.Digest,
|
|
UnitCount: len(document),
|
|
}
|
|
return contracts.CloneSerializedArtifactPointer(artifact), &summary, nil
|
|
}
|