Expose chunk plan provenance and safe diagnostics

This commit is contained in:
2026-07-18 00:45:01 +00:00
parent 8ba5228c01
commit 6fc6ce0adb
15 changed files with 363 additions and 41 deletions

View File

@@ -69,12 +69,42 @@ type RejectedOutputManifest struct {
DiagnosticArtifactPath string `json:"diagnostic_artifact_path,omitempty"`
}
type ChunkPlanManifest struct {
Mode string `json:"mode"`
Action string `json:"action,omitempty"`
SourceDigest string `json:"source_digest,omitempty"`
PlanDigest string `json:"plan_digest,omitempty"`
PlanSchemaVersion string `json:"plan_schema_version,omitempty"`
RequestedModule string `json:"requested_module"`
ProducerInputModule string `json:"producer_input_module,omitempty"`
ProducerModule string `json:"producer_module,omitempty"`
ProducerLLMProfile string `json:"producer_llm_profile,omitempty"`
ProducerReferences []ReferenceProvenance `json:"producer_references,omitempty"`
ProducerMetadata map[string]any `json:"producer_metadata,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
}
// ChunkPlanSummary is deliberately limited to cache and validation decisions.
// It must never contain plan units, source content, annotations, or model I/O.
type ChunkPlanSummary struct {
Mode string `json:"mode"`
SourceDigest string `json:"source_digest,omitempty"`
CandidateDigest string `json:"candidate_digest,omitempty"`
RequestedModule string `json:"requested_module"`
LookupStatus string `json:"lookup_status"`
LookupReason string `json:"lookup_reason,omitempty"`
Action string `json:"action,omitempty"`
ValidationStatus string `json:"validation_status"`
PublicationStatus string `json:"publication_status"`
}
type RunManifest struct {
RunID string `json:"run_id,omitempty"`
PipelineID string `json:"pipeline_id,omitempty"`
PipelineDigest string `json:"pipeline_digest,omitempty"`
InputModule string `json:"input_module,omitempty"`
Chunker string `json:"chunker,omitempty"`
ChunkPlan *ChunkPlanManifest `json:"chunk_plan,omitempty"`
SourceDigests []string `json:"source_digests,omitempty"`
Extractors []string `json:"extractors,omitempty"`
Merger string `json:"merger,omitempty"`

View File

@@ -2,6 +2,7 @@ package artifacts
import (
"encoding/json"
"strings"
"testing"
)
@@ -16,6 +17,37 @@ func TestRunManifestOmitsEmptyOptionalFields(t *testing.T) {
}
}
func TestRunManifestChunkPlanIsAdditiveAndOmitsPlanContent(t *testing.T) {
manifest := RunManifest{ChunkPlan: &ChunkPlanManifest{
Mode: "auto", Action: "reused", SourceDigest: "sha256:source", PlanDigest: "sha256:plan",
PlanSchemaVersion: "notarius.chunk-plan.v1", RequestedModule: "chunk/current",
ProducerInputModule: "input/original", ProducerModule: "chunk/original",
}}
encoded, err := json.Marshal(manifest)
if err != nil {
t.Fatal(err)
}
text := string(encoded)
for _, want := range []string{`"chunk_plan"`, `"action":"reused"`, `"requested_module":"chunk/current"`, `"producer_module":"chunk/original"`} {
if !strings.Contains(text, want) {
t.Fatalf("manifest JSON %s does not contain %s", text, want)
}
}
for _, forbidden := range []string{`"plan"`, `"units"`, `"annotations"`} {
if strings.Contains(text, forbidden) {
t.Fatalf("manifest JSON contains forbidden field %s: %s", forbidden, text)
}
}
var legacy RunManifest
if err := json.Unmarshal([]byte(`{"pipeline_id":"legacy"}`), &legacy); err != nil {
t.Fatal(err)
}
if legacy.PipelineID != "legacy" || legacy.ChunkPlan != nil {
t.Fatalf("legacy manifest = %#v", legacy)
}
}
func TestRunManifestIncludesPipelineAndArtifactLaneFields(t *testing.T) {
manifest := RunManifest{
PipelineID: "pipeline-1",