Update the architecture plan to standardize on input -> chunk -> extract -> merge -> normalize -> output naming conventions

This commit is contained in:
2026-07-03 09:03:45 -05:00
parent 88042174b3
commit 5a6e82f599
18 changed files with 121 additions and 113 deletions

View File

@@ -107,7 +107,7 @@ func (extractor compositionExtractor) Extract(ctx context.Context, req contracts
}
return contracts.ExtractionResult{
Candidates: []artifacts.Candidate{
Candidates: []artifacts.ArtifactCandidate{
{
Index: 0,
ExtractorKey: extractor.Key(),

View File

@@ -53,8 +53,8 @@ type ExtractionRequest struct {
}
type ExtractionResult struct {
Candidates []artifacts.Candidate `json:"candidates,omitempty"`
Warnings []Warning `json:"warnings,omitempty"`
Candidates []artifacts.ArtifactCandidate `json:"candidates,omitempty"`
Warnings []Warning `json:"warnings,omitempty"`
}
type Extractor interface {
@@ -66,9 +66,9 @@ type Extractor interface {
}
type ValidationRequest struct {
Source *source.SourceDocument `json:"-"`
Candidates []artifacts.Candidate `json:"candidates"`
Metadata map[string]any `json:"metadata,omitempty"`
Source *source.SourceDocument `json:"-"`
Candidates []artifacts.ArtifactCandidate `json:"candidates"`
Metadata map[string]any `json:"metadata,omitempty"`
}
type ValidationDecision struct {

View File

@@ -58,19 +58,19 @@ func TestFakeExtractorReturnsCandidateAndValidator(t *testing.T) {
candidate := result.Candidates[0]
if candidate.Index != 0 {
t.Fatalf("Candidate.Index = %d, want 0", candidate.Index)
t.Fatalf("ArtifactCandidate.Index = %d, want 0", candidate.Index)
}
if candidate.ExtractorKey != extractor.Key() {
t.Fatalf("Candidate.ExtractorKey = %q, want %q", candidate.ExtractorKey, extractor.Key())
t.Fatalf("ArtifactCandidate.ExtractorKey = %q, want %q", candidate.ExtractorKey, extractor.Key())
}
if candidate.ArtifactType != extractor.ArtifactType() {
t.Fatalf("Candidate.ArtifactType = %q, want %q", candidate.ArtifactType, extractor.ArtifactType())
t.Fatalf("ArtifactCandidate.ArtifactType = %q, want %q", candidate.ArtifactType, extractor.ArtifactType())
}
if candidate.SchemaVersion != extractor.SchemaVersion() {
t.Fatalf("Candidate.SchemaVersion = %q, want %q", candidate.SchemaVersion, extractor.SchemaVersion())
t.Fatalf("ArtifactCandidate.SchemaVersion = %q, want %q", candidate.SchemaVersion, extractor.SchemaVersion())
}
if string(candidate.Payload) != `{"value":"example"}` {
t.Fatalf("Candidate.Payload = %s, want example payload", candidate.Payload)
t.Fatalf("ArtifactCandidate.Payload = %s, want example payload", candidate.Payload)
}
}
@@ -112,7 +112,7 @@ func (extractor fakeExtractor) Validators() []Validator {
func (extractor fakeExtractor) Extract(ctx context.Context, req ExtractionRequest) (ExtractionResult, error) {
return ExtractionResult{
Candidates: []artifacts.Candidate{
Candidates: []artifacts.ArtifactCandidate{
{
Index: 0,
ExtractorKey: extractor.key,

View File

@@ -82,7 +82,7 @@ func (extractor integrationExtractor) Validators() []contracts.Validator {
func (extractor integrationExtractor) Extract(ctx context.Context, req contracts.ExtractionRequest) (contracts.ExtractionResult, error) {
*extractor.executedKeys = append(*extractor.executedKeys, extractor.key)
return contracts.ExtractionResult{
Candidates: []artifacts.Candidate{
Candidates: []artifacts.ArtifactCandidate{
{Payload: []byte(`{"value":true}`)},
},
}, nil

View File

@@ -90,8 +90,8 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
return output, nil
}
func normalizeCandidates(extractor contracts.Extractor, candidates []artifacts.Candidate, nextIndex *int) ([]artifacts.Candidate, error) {
normalized := make([]artifacts.Candidate, 0, len(candidates))
func normalizeCandidates(extractor contracts.Extractor, candidates []artifacts.ArtifactCandidate, nextIndex *int) ([]artifacts.ArtifactCandidate, error) {
normalized := make([]artifacts.ArtifactCandidate, 0, len(candidates))
for _, candidate := range candidates {
candidate.Index = *nextIndex
*nextIndex = *nextIndex + 1
@@ -119,7 +119,7 @@ func normalizeCandidates(extractor contracts.Extractor, candidates []artifacts.C
return normalized, nil
}
func runValidators(ctx context.Context, extractor contracts.Extractor, doc *source.SourceDocument, candidates []artifacts.Candidate, metadata map[string]any) ([]artifacts.Candidate, []artifacts.RejectedArtifact, []contracts.Warning, error) {
func runValidators(ctx context.Context, extractor contracts.Extractor, doc *source.SourceDocument, candidates []artifacts.ArtifactCandidate, metadata map[string]any) ([]artifacts.ArtifactCandidate, []artifacts.RejectedArtifact, []contracts.Warning, error) {
eligible := candidates
var rejected []artifacts.RejectedArtifact
var warnings []contracts.Warning
@@ -149,7 +149,7 @@ func runValidators(ctx context.Context, extractor contracts.Extractor, doc *sour
decisions[decision.CandidateIndex] = decision
}
nextEligible := make([]artifacts.Candidate, 0, len(eligible))
nextEligible := make([]artifacts.ArtifactCandidate, 0, len(eligible))
for _, candidate := range eligible {
decision := decisions[candidate.Index]
if decision.Approved {

View File

@@ -99,7 +99,7 @@ func TestRunRejectsNilExtractorFromFactory(t *testing.T) {
func TestRunUsesConfiguredExtractorOrderAndAssignsGlobalIndices(t *testing.T) {
var order []string
var seenIndices []int
recordIndices := func(candidates []artifacts.Candidate) []contracts.ValidationDecision {
recordIndices := func(candidates []artifacts.ArtifactCandidate) []contracts.ValidationDecision {
decisions := make([]contracts.ValidationDecision, 0, len(candidates))
for _, candidate := range candidates {
seenIndices = append(seenIndices, candidate.Index)
@@ -133,7 +133,7 @@ func TestRunUsesConfiguredExtractorOrderAndAssignsGlobalIndices(t *testing.T) {
func TestRunFillsEmptyCandidateExtractorMetadata(t *testing.T) {
factory := fakeFactory{extractors: map[string]contracts.Extractor{
"generic-extractor": fakeExtractor{key: "generic-extractor", artifactType: "generic-artifact", schemaVersion: "v1", candidates: []artifacts.Candidate{{Payload: []byte(`{"value":true}`)}}},
"generic-extractor": fakeExtractor{key: "generic-extractor", artifactType: "generic-artifact", schemaVersion: "v1", candidates: []artifacts.ArtifactCandidate{{Payload: []byte(`{"value":true}`)}}},
}}
output, err := New(factory).Run(context.Background(), RunInput{Source: validSourceDocument(), ExtractorKeys: []string{"generic-extractor"}})
@@ -150,18 +150,18 @@ func TestRunFillsEmptyCandidateExtractorMetadata(t *testing.T) {
func TestRunRejectsCandidateMetadataMismatches(t *testing.T) {
tests := []struct {
name string
candidate artifacts.Candidate
candidate artifacts.ArtifactCandidate
error string
}{
{name: "extractor key", candidate: artifacts.Candidate{ExtractorKey: "other"}, error: "extractor_key"},
{name: "artifact type", candidate: artifacts.Candidate{ArtifactType: "other"}, error: "artifact_type"},
{name: "schema version", candidate: artifacts.Candidate{SchemaVersion: "other"}, error: "schema_version"},
{name: "extractor key", candidate: artifacts.ArtifactCandidate{ExtractorKey: "other"}, error: "extractor_key"},
{name: "artifact type", candidate: artifacts.ArtifactCandidate{ArtifactType: "other"}, error: "artifact_type"},
{name: "schema version", candidate: artifacts.ArtifactCandidate{SchemaVersion: "other"}, error: "schema_version"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
factory := fakeFactory{extractors: map[string]contracts.Extractor{
"generic-extractor": fakeExtractor{key: "generic-extractor", artifactType: "generic-artifact", schemaVersion: "v1", candidates: []artifacts.Candidate{tt.candidate}},
"generic-extractor": fakeExtractor{key: "generic-extractor", artifactType: "generic-artifact", schemaVersion: "v1", candidates: []artifacts.ArtifactCandidate{tt.candidate}},
}}
_, err := New(factory).Run(context.Background(), RunInput{Source: validSourceDocument(), ExtractorKeys: []string{"generic-extractor"}})
@@ -194,7 +194,7 @@ func TestRunApprovesCandidatesWithoutValidators(t *testing.T) {
}
func TestRunValidatorApprovalProducesApprovedArtifacts(t *testing.T) {
validator := fakeValidator{name: "generic-validator", decisions: func(candidates []artifacts.Candidate) []contracts.ValidationDecision {
validator := fakeValidator{name: "generic-validator", decisions: func(candidates []artifacts.ArtifactCandidate) []contracts.ValidationDecision {
return []contracts.ValidationDecision{validationhelpers.Approved(candidates[0].Index)}
}}
factory := fakeFactory{extractors: map[string]contracts.Extractor{
@@ -212,13 +212,13 @@ func TestRunValidatorApprovalProducesApprovedArtifacts(t *testing.T) {
func TestRunValidatorRejectionRemovesCandidateFromLaterValidators(t *testing.T) {
var laterSeen int
rejectFirst := fakeValidator{name: "reject-first", decisions: func(candidates []artifacts.Candidate) []contracts.ValidationDecision {
rejectFirst := fakeValidator{name: "reject-first", decisions: func(candidates []artifacts.ArtifactCandidate) []contracts.ValidationDecision {
return []contracts.ValidationDecision{
validationhelpers.Rejected(candidates[0].Index, "invalid", "not accepted"),
validationhelpers.Approved(candidates[1].Index),
}
}}
approveRemaining := fakeValidator{name: "approve-remaining", decisions: func(candidates []artifacts.Candidate) []contracts.ValidationDecision {
approveRemaining := fakeValidator{name: "approve-remaining", decisions: func(candidates []artifacts.ArtifactCandidate) []contracts.ValidationDecision {
laterSeen = len(candidates)
return []contracts.ValidationDecision{validationhelpers.Approved(candidates[0].Index)}
}}
@@ -254,7 +254,7 @@ func TestRunSurfacesValidatorNameMismatch(t *testing.T) {
}
func TestRunSurfacesValidatorCardinalityError(t *testing.T) {
validator := fakeValidator{name: "generic-validator", decisions: func(candidates []artifacts.Candidate) []contracts.ValidationDecision {
validator := fakeValidator{name: "generic-validator", decisions: func(candidates []artifacts.ArtifactCandidate) []contracts.ValidationDecision {
return nil
}}
factory := factoryWithValidator(validator)
@@ -359,7 +359,7 @@ type fakeExtractor struct {
artifactType string
schemaVersion string
candidateCount int
candidates []artifacts.Candidate
candidates []artifacts.ArtifactCandidate
validators []contracts.Validator
warnings []contracts.Warning
err error
@@ -386,9 +386,9 @@ func (extractor fakeExtractor) Extract(ctx context.Context, req contracts.Extrac
if extractor.order != nil {
*extractor.order = append(*extractor.order, extractor.key)
}
candidates := append([]artifacts.Candidate(nil), extractor.candidates...)
candidates := append([]artifacts.ArtifactCandidate(nil), extractor.candidates...)
for len(candidates) < extractor.candidateCount {
candidates = append(candidates, artifacts.Candidate{Payload: []byte(`{"value":true}`)})
candidates = append(candidates, artifacts.ArtifactCandidate{Payload: []byte(`{"value":true}`)})
}
return contracts.ExtractionResult{
Candidates: candidates,
@@ -399,7 +399,7 @@ func (extractor fakeExtractor) Extract(ctx context.Context, req contracts.Extrac
type fakeValidator struct {
name string
resultName string
decisions func([]artifacts.Candidate) []contracts.ValidationDecision
decisions func([]artifacts.ArtifactCandidate) []contracts.ValidationDecision
warnings []contracts.Warning
err error
}
@@ -436,7 +436,7 @@ func factoryWithValidator(validator contracts.Validator) fakeFactory {
}}
}
func approveAll(candidates []artifacts.Candidate) []contracts.ValidationDecision {
func approveAll(candidates []artifacts.ArtifactCandidate) []contracts.ValidationDecision {
decisions := make([]contracts.ValidationDecision, 0, len(candidates))
for _, candidate := range candidates {
decisions = append(decisions, validationhelpers.Approved(candidate.Index))

View File

@@ -30,7 +30,7 @@ func Rejected(candidateIndex int, reasonCode string, message string) contracts.V
}
}
func EnforceDecisionCardinality(candidates []artifacts.Candidate, decisions []contracts.ValidationDecision) error {
func EnforceDecisionCardinality(candidates []artifacts.ArtifactCandidate, decisions []contracts.ValidationDecision) error {
if len(candidates) != len(decisions) {
return fmt.Errorf("validator returned %d decisions for %d candidates", len(decisions), len(candidates))
}

View File

@@ -43,7 +43,7 @@ func TestRejectedTrimsReasonAndMessage(t *testing.T) {
}
func TestEnforceDecisionCardinalityAllowsNonZeroCandidateIndices(t *testing.T) {
candidates := []artifacts.Candidate{{Index: 4}, {Index: 8}}
candidates := []artifacts.ArtifactCandidate{{Index: 4}, {Index: 8}}
decisions := []contracts.ValidationDecision{Approved(8), Approved(4)}
if err := EnforceDecisionCardinality(candidates, decisions); err != nil {
@@ -59,7 +59,7 @@ func TestEnforceDecisionCardinalityAllowsEmptyInputs(t *testing.T) {
func TestEnforceDecisionCardinalityRejectsUnknownDecisionIndex(t *testing.T) {
err := EnforceDecisionCardinality(
[]artifacts.Candidate{{Index: 1}},
[]artifacts.ArtifactCandidate{{Index: 1}},
[]contracts.ValidationDecision{Approved(2)},
)
@@ -68,7 +68,7 @@ func TestEnforceDecisionCardinalityRejectsUnknownDecisionIndex(t *testing.T) {
func TestEnforceDecisionCardinalityRejectsDuplicateDecisionIndex(t *testing.T) {
err := EnforceDecisionCardinality(
[]artifacts.Candidate{{Index: 1}, {Index: 2}},
[]artifacts.ArtifactCandidate{{Index: 1}, {Index: 2}},
[]contracts.ValidationDecision{Approved(1), Approved(1)},
)
@@ -77,7 +77,7 @@ func TestEnforceDecisionCardinalityRejectsDuplicateDecisionIndex(t *testing.T) {
func TestEnforceDecisionCardinalityRejectsMissingDecisionIndex(t *testing.T) {
err := EnforceDecisionCardinality(
[]artifacts.Candidate{{Index: 1}, {Index: 2}},
[]artifacts.ArtifactCandidate{{Index: 1}, {Index: 2}},
[]contracts.ValidationDecision{Approved(1)},
)
@@ -86,7 +86,7 @@ func TestEnforceDecisionCardinalityRejectsMissingDecisionIndex(t *testing.T) {
func TestEnforceDecisionCardinalityRejectsDuplicateCandidateIndex(t *testing.T) {
err := EnforceDecisionCardinality(
[]artifacts.Candidate{{Index: 1}, {Index: 1}},
[]artifacts.ArtifactCandidate{{Index: 1}, {Index: 1}},
[]contracts.ValidationDecision{Approved(1), Approved(1)},
)