From 7c94b5eeedc471c2704e9c2d00a61cab75b98c95 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Mon, 27 Jul 2026 14:03:23 -0500 Subject: [PATCH] Clarify evidence context and test lane filtering --- docs/integrations/evidence-context.md | 12 +++---- .../pipeline/evidence_preparation_test.go | 31 ++++++++++++++++--- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/integrations/evidence-context.md b/docs/integrations/evidence-context.md index 82d4e12..fc3067b 100644 --- a/docs/integrations/evidence-context.md +++ b/docs/integrations/evidence-context.md @@ -81,12 +81,12 @@ evidence publishes `contexts: []`. } ``` -Each context requires `context_ref`, `evidence_refs`, and `units` arrays. -`context_ref` identifies the first and last included unit. Each evidence entry -contains a selected `lane_id` and an original `source_ref`. A unit uses the -existing source-unit shape: required `id`, `kind`, `text`, and self `ref`, plus -optional JSON-object `metadata`. Fixed payload objects reject unknown fields; -unit metadata may contain application-defined JSON values. +Each context requires a `context_ref` object and `evidence_refs` and `units` +arrays. `context_ref` identifies the first and last included unit. Each +evidence entry contains a selected `lane_id` and an original `source_ref`. A +unit uses the existing source-unit shape: required `id`, `kind`, `text`, and +self `ref`, plus optional JSON-object `metadata`. Fixed payload objects reject +unknown fields; unit metadata may contain application-defined JSON values. ## Citations And Context diff --git a/internal/framework/pipeline/evidence_preparation_test.go b/internal/framework/pipeline/evidence_preparation_test.go index 8a5f7a7..7db87e9 100644 --- a/internal/framework/pipeline/evidence_preparation_test.go +++ b/internal/framework/pipeline/evidence_preparation_test.go @@ -2,6 +2,7 @@ package pipeline import ( "context" + "fmt" "reflect" "strings" "testing" @@ -22,19 +23,34 @@ func (output testEvidenceOutput) EvidenceContextPolicy() EvidenceContextPolicy { return cloneEvidenceContextPolicy(output.policy) } -func TestPrepareEvidencePlanSelectsActiveLanesAndOwnsPolicy(t *testing.T) { +func TestEvidencePolicyResolutionAndPreparationSurviveLaneFiltering(t *testing.T) { registries, _ := constructionRegistries(t, nil, nil) registries.ArtifactEvidence = NewArtifactEvidenceRegistry() - registerTestEvidenceOutput(t, ®istries, EvidenceContextPolicy{Enabled: true, WindowUnits: 2, LaneIDs: []string{"artifact", "inactive"}}) + policy := EvidenceContextPolicy{Enabled: true, WindowUnits: 2, LaneIDs: []string{"artifact", "inactive"}} + profileValidated := false + registerTestEvidenceOutputWithProfileValidation(t, ®istries, policy, func(context OutputProfileOptionContext, _ map[string]any) error { + profileValidated = true + if want := []string{"artifact", "inactive"}; !reflect.DeepEqual(context.LaneIDs, want) { + return fmt.Errorf("configured lane ids = %#v, want %#v", context.LaneIDs, want) + } + return nil + }) if err := RegisterArtifactEvidence(registries.ArtifactEvidence, "test/notes", func(codecNotes) []source.SourceRef { return nil }); err != nil { t.Fatal(err) } profile := constructionProfile() + profile.Artifacts["inactive"] = profile.Artifacts["artifact"] profile.Output.Options = map[string]any{"known": true} - resolved, err := ResolvePipeline(profile, ResolveOptions{}, registries.catalog()) + resolved, err := ResolvePipeline(profile, ResolveOptions{Only: []string{"artifact"}}, registries.catalog()) if err != nil { t.Fatal(err) } + if !profileValidated { + t.Fatal("output profile options were not validated") + } + if lanes := resolved.AllArtifactLanes(); len(lanes) != 1 || lanes[0].ID != "artifact" { + t.Fatalf("resolved lanes = %#v, want only the invocation-selected lane", lanes) + } prepared, err := Prepare(resolved, registries, ModuleDependencies{}) if err != nil { t.Fatalf("Prepare() error = %v, want nil", err) @@ -85,11 +101,16 @@ func TestPrepareEvidencePlanRejectsMissingAndMismatchedCapabilities(t *testing.T } func registerTestEvidenceOutput(t *testing.T, registries *Registries, policy EvidenceContextPolicy) { + t.Helper() + registerTestEvidenceOutputWithProfileValidation(t, registries, policy, nil) +} + +func registerTestEvidenceOutputWithProfileValidation(t *testing.T, registries *Registries, policy EvidenceContextPolicy, validateProfile OutputProfileOptionValidator) { t.Helper() registry := NewOutputEncoderRegistry() - if err := registry.RegisterBuilderWithSpec(defaultModuleSpec("output", StageOutput), func(options map[string]any) error { + if err := registry.RegisterBuilderWithProfileValidation(defaultModuleSpec("output", StageOutput), func(options map[string]any) error { return RejectUnknownOptions(options, "known") - }, func(BuildRequest) (contracts.OutputEncoder, error) { + }, validateProfile, func(BuildRequest) (contracts.OutputEncoder, error) { return testEvidenceOutput{policy: cloneEvidenceContextPolicy(policy)}, nil }); err != nil { t.Fatal(err)