package runner import ( "context" "errors" "os" "path/filepath" "regexp" "strconv" "strings" "sync" "sync/atomic" "testing" "time" "gitea.maximumdirect.net/eric/audita/internal/core/config" "gitea.maximumdirect.net/eric/audita/internal/core/schema" "gitea.maximumdirect.net/eric/audita/internal/framework/contracts" "gitea.maximumdirect.net/eric/audita/internal/framework/llm" "gitea.maximumdirect.net/eric/audita/internal/framework/modules" "gitea.maximumdirect.net/eric/audita/internal/framework/proposal_generation" "gitea.maximumdirect.net/eric/audita/internal/framework/proposals" "gitea.maximumdirect.net/eric/audita/internal/framework/responseschema" "gitea.maximumdirect.net/eric/audita/internal/framework/validators" validatormetadata "gitea.maximumdirect.net/eric/audita/internal/validators/metadata" ) type fakeFactory struct { modules map[string]contracts.TranscriptModule } func (f fakeFactory) ModuleForSpec(spec contracts.ModuleRunSpec) (contracts.TranscriptModule, error) { m, ok := f.modules[spec.InstanceName] if !ok { return nil, errors.New("module not registered") } return m, nil } type fakeModule struct { key string policy proposals.ReplacementPolicy validators []contracts.Validator proposeF func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) } func (m fakeModule) Key() string { return m.key } func (m fakeModule) ReplacementPolicy() proposals.ReplacementPolicy { return m.policy } func (m fakeModule) Validators() []contracts.Validator { return m.validators } func (m fakeModule) Propose(ctx context.Context, req contracts.ProposalRequest) (contracts.ProposalResult, error) { if m.proposeF == nil { return contracts.ProposalResult{}, nil } proposalsOut, err := m.proposeF(req) return contracts.ProposalResult{Proposals: proposalsOut}, err } type fakeValidator struct { name string validateF func(req contracts.ValidationRequest) (validators.Result, error) } func (v fakeValidator) Name() string { return v.name } func (v fakeValidator) Validate(ctx context.Context, req contracts.ValidationRequest) (validators.Result, error) { _ = ctx return v.validateF(req) } type classifiedFakeValidator struct { fakeValidator class validatormetadata.ExecutionClass } func (v classifiedFakeValidator) ExecutionClass() validatormetadata.ExecutionClass { return v.class } func TestReorderValidatorsDeterministicBeforeLLMBacked(t *testing.T) { llm := classifiedFakeValidator{ fakeValidator: fakeValidator{name: "llm", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { return validators.Result{ValidatorName: "llm"}, nil }}, class: validatormetadata.ExecutionClassLLMBacked, } deterministic := classifiedFakeValidator{ fakeValidator: fakeValidator{name: "deterministic", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { return validators.Result{ValidatorName: "deterministic"}, nil }}, class: validatormetadata.ExecutionClassDeterministic, } ordered, _ := reorderValidatorsForPipeline([]contracts.Validator{llm, deterministic}) if len(ordered) != 2 { t.Fatalf("expected 2 validators, got %d", len(ordered)) } if ordered[0].Name() != "deterministic" || ordered[1].Name() != "llm" { t.Fatalf("unexpected validator order: %s, %s", ordered[0].Name(), ordered[1].Name()) } } func TestReorderValidatorsDefaultsUnclassifiedToDeterministic(t *testing.T) { unclassified := fakeValidator{name: "plain", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { return validators.Result{ValidatorName: "plain"}, nil }} llm := classifiedFakeValidator{ fakeValidator: fakeValidator{name: "llm", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { return validators.Result{ValidatorName: "llm"}, nil }}, class: validatormetadata.ExecutionClassLLMBacked, } ordered, _ := reorderValidatorsForPipeline([]contracts.Validator{llm, unclassified}) if len(ordered) != 2 { t.Fatalf("expected 2 validators, got %d", len(ordered)) } if ordered[0].Name() != "plain" || ordered[1].Name() != "llm" { t.Fatalf("unexpected validator order with unclassified validator: %s, %s", ordered[0].Name(), ordered[1].Name()) } } func TestRunnerOneModuleAppliesProposal(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh cat"}}} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "grammar": fakeModule{key: "grammar", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 0.9}}, nil }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "the cat" { t.Fatalf("expected corrected text, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults) != 1 || len(out.ModuleResults[0].AppliedChanges) != 1 { t.Fatalf("expected one module with one applied change, got %+v", out.ModuleResults) } } func TestRunnerModulesRunSequentially(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh cat"}}} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m1": fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }}, "m2": fakeModule{key: "m2", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { if req.WorkingTranscript.Segments[0].Text != "the cat" { t.Fatalf("second module did not see first module changes: %q", req.WorkingTranscript.Segments[0].Text) } return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: 1}}, nil }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m1", InstanceName: "m1"}, {ModuleKey: "m2", InstanceName: "m2"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "the dog" { t.Fatalf("expected sequential updates, got %q", out.FinalTranscript.Segments[0].Text) } } func TestRunnerProposalsExecutePerChunkSection(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "one two"}, {ID: 2, Text: "three four"}, {ID: 3, Text: "five six"}, }} var ( mu sync.Mutex seenBySection = make(map[int]contracts.SectionMetadata) segmentCountBySec = make(map[int]int) ) r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { if req.Section == nil { t.Fatalf("expected section metadata on proposal request") } mu.Lock() seenBySection[req.Section.Index] = *req.Section segmentCountBySec[req.Section.Index] = len(req.WorkingTranscript.Segments) mu.Unlock() return nil, nil }}, }}) out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{ {ModuleKey: "m", InstanceName: "m"}, }, }) if err != nil { t.Fatalf("Run error: %v", err) } if len(out.ModuleResults) != 1 { t.Fatalf("expected one module result, got %+v", out.ModuleResults) } if len(seenBySection) != 3 { t.Fatalf("expected 3 chunked proposal calls, got %d", len(seenBySection)) } for i := 0; i < 3; i++ { section, ok := seenBySection[i] if !ok { t.Fatalf("expected section %d to be observed", i) } if section.Index != i { t.Fatalf("expected section index %d, got %+v", i, section) } if segmentCountBySec[i] != 1 { t.Fatalf("expected one segment per section call, got %d at index %d", segmentCountBySec[i], i) } } } func TestRunnerProposalSectionConcurrencyBoundedByProposalLLMConcurrency(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 cfg.TotalLLMConcurrency = 2 cfg.ProposalLLMConcurrency = 2 transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "one two"}, {ID: 2, Text: "three four"}, {ID: 3, Text: "five six"}, {ID: 4, Text: "seven eight"}, }} var inFlight int32 var maxInFlight int32 entered := make(chan struct{}, len(transcript.Segments)) release := make(chan struct{}) scheduler, err := llm.NewScheduler(2) if err != nil { t.Fatalf("NewScheduler: %v", err) } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { err := req.LLMScheduler.Run(context.Background(), func(context.Context) error { current := atomic.AddInt32(&inFlight, 1) for { prior := atomic.LoadInt32(&maxInFlight) if current <= prior || atomic.CompareAndSwapInt32(&maxInFlight, prior, current) { break } } entered <- struct{}{} <-release atomic.AddInt32(&inFlight, -1) return nil }) if err != nil { return nil, err } return nil, nil }}, }}) resultCh := make(chan error, 1) go func() { _, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ProposalLLMScheduler: scheduler, ModuleSpecs: []contracts.ModuleRunSpec{ {ModuleKey: "m", InstanceName: "m"}, }, }) resultCh <- err }() waitForRunnerEntries(t, entered, 2, "proposal workers to enter") close(release) err = <-resultCh if err != nil { t.Fatalf("Run error: %v", err) } if maxInFlight < 2 { t.Fatalf("expected proposal execution to run concurrently, got max in-flight %d", maxInFlight) } if maxInFlight > 2 { t.Fatalf("expected proposal concurrency <= 2, got %d", maxInFlight) } } func TestRunnerProposalIndexOrderingIsDeterministicAcrossParallelSections(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 cfg.TotalLLMConcurrency = 3 cfg.ProposalLLMConcurrency = 3 transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "alpha one"}, {ID: 2, Text: "bravo two"}, {ID: 3, Text: "charlie three"}, }} started := make(chan int, len(transcript.Segments)) releaseByID := map[int]chan struct{}{ 1: make(chan struct{}), 2: make(chan struct{}), 3: make(chan struct{}), } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { if len(req.WorkingTranscript.Segments) != 1 { t.Fatalf("expected one segment per section, got %d", len(req.WorkingTranscript.Segments)) } seg := req.WorkingTranscript.Segments[0] started <- seg.ID <-releaseByID[seg.ID] return []proposals.CorrectionProposal{ {TargetSegmentID: seg.ID, OriginalText: seg.Text, CorrectedText: strings.ToUpper(seg.Text), Confidence: 1}, }, nil }}, }}) resultCh := make(chan struct { out RunOutput err error }, 1) go func() { out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{ {ModuleKey: "m", InstanceName: "m"}, }, }) resultCh <- struct { out RunOutput err error }{out: out, err: err} }() waitForRunnerSectionIDs(t, started, map[int]struct{}{1: {}, 2: {}, 3: {}}) close(releaseByID[3]) close(releaseByID[2]) close(releaseByID[1]) result := <-resultCh out, err := result.out, result.err if err != nil { t.Fatalf("Run error: %v", err) } if len(out.ModuleResults) != 1 { t.Fatalf("expected one module result, got %+v", out.ModuleResults) } if got := len(out.ModuleResults[0].AppliedChanges); got != 3 { t.Fatalf("expected three applied changes, got %d", got) } for i, change := range out.ModuleResults[0].AppliedChanges { wantID := i + 1 if change.TargetSegmentID != wantID { t.Fatalf("expected deterministic applied-change order by section/segment, got %+v", out.ModuleResults[0].AppliedChanges) } } wantFinal := []string{"ALPHA ONE", "BRAVO TWO", "CHARLIE THREE"} for i, seg := range out.FinalTranscript.Segments { if seg.Text != wantFinal[i] { t.Fatalf("expected deterministic final transcript regardless of section completion order; got %+v", out.FinalTranscript.Segments) } } } func waitForRunnerEntries(t *testing.T, entered <-chan struct{}, want int, label string) { t.Helper() deadline := time.After(350 * time.Millisecond) got := 0 for got < want { select { case <-entered: got++ case <-deadline: t.Fatalf("timed out waiting for %d entries (%s), got %d", want, label, got) } } } func waitForRunnerSectionIDs(t *testing.T, started <-chan int, want map[int]struct{}) { t.Helper() deadline := time.After(350 * time.Millisecond) seen := map[int]struct{}{} for len(seen) < len(want) { select { case id := <-started: seen[id] = struct{}{} case <-deadline: t.Fatalf("timed out waiting for section IDs %v, got %v", mapKeys(want), mapKeys(seen)) } } } func mapKeys(values map[int]struct{}) []int { keys := make([]int, 0, len(values)) for key := range values { keys = append(keys, key) } return keys } func TestRunnerValidationStartsBeforeAllSectionProposalsComplete(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 cfg.TotalLLMConcurrency = 2 cfg.ProposalLLMConcurrency = 2 transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "alpha one"}, {ID: 2, Text: "bravo two"}, }} releaseSectionOne := make(chan struct{}) validatorStarted := make(chan struct{}, 1) validator := fakeValidator{ name: "capture", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { if len(req.CandidateProposal) == 1 && req.CandidateProposal[0].TargetSegmentID == 1 { select { case validatorStarted <- struct{}{}: default: } } decisions := make([]validators.Decision, 0, len(req.CandidateProposal)) for _, p := range req.CandidateProposal { decisions = append(decisions, validators.Decision{ ProposalIndex: p.ProposalIndex, Approved: true, ReasonCode: validators.ReasonApproved, Message: "ok", }) } return validators.Result{ValidatorName: "capture", Decisions: decisions}, nil }, } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{ key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{validator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { seg := req.WorkingTranscript.Segments[0] if req.Section != nil && req.Section.Index == 1 { <-releaseSectionOne } return []proposals.CorrectionProposal{ {TargetSegmentID: seg.ID, OriginalText: seg.Text, CorrectedText: strings.ToUpper(seg.Text), Confidence: 1}, }, nil }, }, }}) resultCh := make(chan error, 1) go func() { _, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{ {ModuleKey: "m", InstanceName: "m"}, }, }) resultCh <- err }() select { case <-validatorStarted: case <-time.After(350 * time.Millisecond): t.Fatal("expected section-level validator work before all proposal sections complete") } close(releaseSectionOne) if err := <-resultCh; err != nil { t.Fatalf("Run error: %v", err) } } func TestRunnerProposalAndValidationLLMCanOverlapWithinModule(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 cfg.TotalLLMConcurrency = 2 cfg.ProposalLLMConcurrency = 2 validationCap := 2 cfg.ValidationLLMConcurrency = &validationCap transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "alpha one"}, {ID: 2, Text: "bravo two"}, }} global, err := llm.NewScheduler(2) if err != nil { t.Fatalf("NewScheduler: %v", err) } scheduler := &trackingScheduler{inner: global} proposalSectionOneStarted := make(chan struct{}, 1) releaseProposalSectionOne := make(chan struct{}) releaseValidation := make(chan struct{}) client := &stageAwareStructuredClient{ startedSection: make(chan int, 8), releaseValidation: releaseValidation, } llmValidator, err := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") if err != nil { t.Fatalf("NewLLMBackedValidator: %v", err) } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{ key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { seg := req.WorkingTranscript.Segments[0] err := req.LLMScheduler.Run(context.Background(), func(context.Context) error { if req.Section != nil && req.Section.Index == 1 { select { case proposalSectionOneStarted <- struct{}{}: default: } <-releaseProposalSectionOne } return nil }) if err != nil { return nil, err } return []proposals.CorrectionProposal{ {TargetSegmentID: seg.ID, OriginalText: seg.Text, CorrectedText: strings.ToUpper(seg.Text), Confidence: 1}, }, nil }, }, }}) resultCh := make(chan struct { out RunOutput err error }, 1) go func() { out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ProposalLLMScheduler: scheduler, ValidationLLMScheduler: scheduler, ValidationLLMClient: client, }) resultCh <- struct { out RunOutput err error }{out: out, err: err} }() select { case <-proposalSectionOneStarted: case <-time.After(350 * time.Millisecond): t.Fatal("expected section-1 proposal to start") } select { case section := <-client.startedSection: if section != 0 { t.Fatalf("expected first validation to start for section 0, got section %d", section) } case <-time.After(350 * time.Millisecond): t.Fatal("expected validation call for section 0 while later section proposal still running") } close(releaseProposalSectionOne) close(releaseValidation) result := <-resultCh if result.err != nil { t.Fatalf("Run error: %v", result.err) } if got := atomic.LoadInt32(&scheduler.maxInFlight); got > 2 { t.Fatalf("expected combined proposal+validation in-flight <= 2, got %d", got) } if got := atomic.LoadInt32(&scheduler.maxInFlight); got < 2 { t.Fatalf("expected observed overlap/in-flight utilization of at least 2, got %d", got) } } func TestRunnerValidationLLMConcurrencyRespected(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 cfg.TotalLLMConcurrency = 4 cfg.ProposalLLMConcurrency = 4 validationCap := 1 cfg.ValidationLLMConcurrency = &validationCap transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "one one"}, {ID: 2, Text: "two two"}, {ID: 3, Text: "three three"}, }} validationSchedulerInner, err := llm.NewScheduler(1) if err != nil { t.Fatalf("NewScheduler(validation): %v", err) } validationScheduler := &trackingScheduler{inner: validationSchedulerInner} releaseValidation := make(chan struct{}) client := &stageAwareStructuredClient{ startedSection: make(chan int, 16), releaseValidation: releaseValidation, } llmValidator, err := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") if err != nil { t.Fatalf("NewLLMBackedValidator: %v", err) } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{ key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { seg := req.WorkingTranscript.Segments[0] return []proposals.CorrectionProposal{ {TargetSegmentID: seg.ID, OriginalText: seg.Text, CorrectedText: strings.ToUpper(seg.Text), Confidence: 1}, }, nil }, }, }}) resultCh := make(chan error, 1) go func() { _, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMScheduler: validationScheduler, ValidationLLMClient: client, }) resultCh <- err }() select { case <-client.startedSection: case <-time.After(350 * time.Millisecond): t.Fatal("expected validation to start") } close(releaseValidation) if err := <-resultCh; err != nil { t.Fatalf("Run error: %v", err) } if got := atomic.LoadInt32(&validationScheduler.maxInFlight); got > 1 { t.Fatalf("expected validation in-flight <= 1, got %d", got) } } func TestRunnerMixedProposalValidationFIFOOrder(t *testing.T) { cfg := config.Default() cfg.MaxSectionTokens = 3 cfg.MinSectionTokens = 0 cfg.TotalLLMConcurrency = 1 cfg.ProposalLLMConcurrency = 1 validationCap := 1 cfg.ValidationLLMConcurrency = &validationCap transcript := &schema.Transcript{Segments: []schema.Segment{ {ID: 1, Text: "alpha one"}, {ID: 2, Text: "bravo two"}, }} global, err := llm.NewScheduler(1) if err != nil { t.Fatalf("NewScheduler: %v", err) } proposalScheduler := global validationScheduler := global events := make(chan string, 8) releaseProposalSectionOne := make(chan struct{}) releaseValidation := make(chan struct{}) sectionZeroEntered := make(chan struct{}) sectionOneAttempted := make(chan struct{}, 1) client := &stageAwareStructuredClient{ startedSection: make(chan int, 8), releaseValidation: releaseValidation, eventSink: events, } llmValidator, err := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") if err != nil { t.Fatalf("NewLLMBackedValidator: %v", err) } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{ key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { seg := req.WorkingTranscript.Segments[0] if req.Section != nil && req.Section.Index == 1 { <-sectionZeroEntered select { case sectionOneAttempted <- struct{}{}: default: } } err := req.LLMScheduler.Run(context.Background(), func(context.Context) error { if req.Section != nil { events <- "p" + strconv.Itoa(req.Section.Index) if req.Section.Index == 0 { select { case sectionZeroEntered <- struct{}{}: default: } <-sectionOneAttempted } if req.Section.Index == 1 { <-releaseProposalSectionOne } } return nil }) if err != nil { return nil, err } return []proposals.CorrectionProposal{ {TargetSegmentID: seg.ID, OriginalText: seg.Text, CorrectedText: strings.ToUpper(seg.Text), Confidence: 1}, }, nil }, }, }}) resultCh := make(chan error, 1) go func() { _, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ProposalLLMScheduler: proposalScheduler, ValidationLLMScheduler: validationScheduler, ValidationLLMClient: client, }) resultCh <- err }() if got := <-events; got != "p0" { t.Fatalf("expected first event p0, got %q", got) } if got := <-events; got != "p1" { t.Fatalf("expected second event p1 (FIFO queued proposal), got %q", got) } close(releaseProposalSectionOne) close(releaseValidation) if got := <-events; !strings.HasPrefix(got, "v") { t.Fatalf("expected validator event after queued p1, got %q", got) } if err := <-resultCh; err != nil { t.Fatalf("Run error: %v", err) } } var sectionStagePattern = regexp.MustCompile(`section-(\d+)`) type stageAwareStructuredClient struct { startedSection chan int releaseValidation <-chan struct{} eventSink chan<- string } type captureContractStructuredClient struct { lastRequest contracts.StructuredCompletionRequest } func (c *captureContractStructuredClient) CompleteStructured(ctx context.Context, req contracts.StructuredCompletionRequest, out any) (contracts.StructuredCompletionResponse, error) { _ = ctx c.lastRequest = req if target, ok := out.(*validators.LLMValidationResponse); ok { *target = validators.LLMValidationResponse{ Validations: []validators.LLMValidationDecision{ {CorrectionIndex: 0, Approved: true, Confidence: 0.9, Reason: "ok"}, }, } } return contracts.StructuredCompletionResponse{}, nil } func (c *stageAwareStructuredClient) CompleteStructured(ctx context.Context, req contracts.StructuredCompletionRequest, out any) (contracts.StructuredCompletionResponse, error) { section := parseSectionFromStage(req.StageName) if c.startedSection != nil { select { case c.startedSection <- section: default: } } if c.eventSink != nil { c.eventSink <- "v" + strconv.Itoa(section) } if c.releaseValidation != nil { select { case <-c.releaseValidation: case <-ctx.Done(): return contracts.StructuredCompletionResponse{}, ctx.Err() } } target := out.(*validators.LLMValidationResponse) *target = validators.LLMValidationResponse{ Validations: []validators.LLMValidationDecision{ {CorrectionIndex: section, Approved: true, Confidence: 0.99, Reason: "ok"}, }, } return contracts.StructuredCompletionResponse{}, nil } func parseSectionFromStage(stage string) int { match := sectionStagePattern.FindStringSubmatch(stage) if len(match) != 2 { return 0 } n, err := strconv.Atoi(match[1]) if err != nil { return 0 } return n } func TestValidationLLMClientAdapterPassesResponseSchema(t *testing.T) { capture := &captureContractStructuredClient{} adapter := validationLLMClientAdapter{client: capture} schema := responseschema.MustLookup(responseschema.ValidatorDecisionSetKey) _, err := adapter.CompleteStructured(context.Background(), validators.StructuredCompletionRequest{ StageName: "module:validator:batch-0000", Messages: []validators.LLMMessage{ {Role: "system", Content: "system"}, {Role: "user", Content: "user"}, }, Model: "test-model", ResponseSchema: &schema, }, &validators.LLMValidationResponse{}) if err != nil { t.Fatalf("CompleteStructured error: %v", err) } if capture.lastRequest.ResponseSchema == nil { t.Fatalf("expected response schema to be forwarded") } if capture.lastRequest.ResponseSchema.ID != schema.ID || capture.lastRequest.ResponseSchema.Version != schema.Version || capture.lastRequest.ResponseSchema.Name != schema.Name || capture.lastRequest.ResponseSchema.SHA256 != schema.SHA256 { t.Fatalf("unexpected forwarded schema metadata: got=%+v want=%+v", *capture.lastRequest.ResponseSchema, schema) } } type trackingScheduler struct { inner contracts.LLMScheduler inFlight int32 maxInFlight int32 } func (s *trackingScheduler) Run(ctx context.Context, fn func(context.Context) error) error { return s.inner.Run(ctx, func(callCtx context.Context) error { current := atomic.AddInt32(&s.inFlight, 1) for { prior := atomic.LoadInt32(&s.maxInFlight) if current <= prior || atomic.CompareAndSwapInt32(&s.maxInFlight, prior, current) { break } } defer atomic.AddInt32(&s.inFlight, -1) return fn(callCtx) }) } func TestRunnerSkippedRecorded(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "word word"}}} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "word", CorrectedText: "term", Confidence: 1}}, nil }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if len(out.ModuleResults[0].SkippedChanges) != 1 { t.Fatalf("expected one skipped change, got %+v", out.ModuleResults[0].SkippedChanges) } } func TestRunnerFailureReturnsPartialProgress(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh cat"}}} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m1": fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }}, "m2": fakeModule{key: "m2", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return nil, errors.New("boom") }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m1", InstanceName: "m1"}, {ModuleKey: "m2", InstanceName: "m2"}}}) if err == nil { t.Fatal("expected error") } if out.FinalTranscript.Segments[0].Text != "the cat" { t.Fatalf("expected partial transcript progress preserved, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults) != 2 || out.ModuleResults[1].Status != ModuleStatusFailed { t.Fatalf("expected second module failed in results, got %+v", out.ModuleResults) } } func TestRunnerDoesNotMutateInputTranscript(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Speaker: "A", Start: 0, End: 1, Text: "teh"}}} before := transcript.Segments[0].Text r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }}, }}) _, err := r.Run(context.Background(), RunInput{Config: ptrConfig(config.Default()), Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if transcript.Segments[0].Text != before { t.Fatalf("expected input transcript unchanged, got %q", transcript.Segments[0].Text) } } func TestRunnerRepeatedModuleInstanceNames(t *testing.T) { specs, err := contracts.ResolveModuleRunSpecs([]string{"glossary", "glossary"}) if err != nil { t.Fatalf("ResolveModuleRunSpecs error: %v", err) } if specs[0].InstanceName != "glossary_1" || specs[1].InstanceName != "glossary_2" { t.Fatalf("unexpected instance names: %+v", specs) } } func TestRunnerValidatorApprovedProposalApplied(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "teh cat"}}} allowAll := fakeValidator{name: "allow", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { decisions := make([]validators.Decision, len(req.CandidateProposal)) for i, p := range req.CandidateProposal { decisions[i] = validators.Decision{ProposalIndex: p.ProposalIndex, Approved: true, ReasonCode: validators.ReasonApproved, Message: "approved"} } return validators.Result{ValidatorName: "allow", Decisions: decisions}, nil }} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{ key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{allowAll}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }, }, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "the cat" { t.Fatalf("expected proposal applied, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults[0].ValidatorDecisions) != 1 { t.Fatalf("expected validator decisions recorded") } } func TestRunnerValidatorRejectedProposalNotApplied(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "teh cat"}}} rejectAll := fakeValidator{name: "reject", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { decisions := make([]validators.Decision, len(req.CandidateProposal)) for i, p := range req.CandidateProposal { decisions[i] = validators.Decision{ProposalIndex: p.ProposalIndex, Approved: false, ReasonCode: validators.ReasonNoEffect, Message: "rejected"} } return validators.Result{ValidatorName: "reject", Decisions: decisions}, nil }} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{rejectAll}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "teh cat" { t.Fatalf("expected rejected proposal not applied, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults[0].ValidatorRejected) != 1 { t.Fatalf("expected validator rejection recorded, got %+v", out.ModuleResults[0].ValidatorRejected) } } func TestRunnerMultipleValidatorsRunInOrderAndFilterSurvivors(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "one two"}}} first := fakeValidator{name: "first", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { if len(req.CandidateProposal) != 2 { t.Fatalf("expected first validator to see 2 candidates, got %d", len(req.CandidateProposal)) } return validators.Result{ ValidatorName: "first", Decisions: []validators.Decision{ {ProposalIndex: req.CandidateProposal[0].ProposalIndex, Approved: true, ReasonCode: validators.ReasonApproved, Message: "ok"}, {ProposalIndex: req.CandidateProposal[1].ProposalIndex, Approved: false, ReasonCode: validators.ReasonNoEffect, Message: "reject"}, }, }, nil }} second := fakeValidator{name: "second", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { if len(req.CandidateProposal) != 1 { t.Fatalf("expected second validator to see only survivors, got %d", len(req.CandidateProposal)) } return validators.Result{ValidatorName: "second", Decisions: []validators.Decision{{ProposalIndex: req.CandidateProposal[0].ProposalIndex, Approved: true, ReasonCode: validators.ReasonApproved, Message: "ok"}}}, nil }} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{first, second}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{ {TargetSegmentID: 1, OriginalText: "one", CorrectedText: "ONE", Confidence: 1}, {TargetSegmentID: 1, OriginalText: "two", CorrectedText: "TWO", Confidence: 1}, }, nil }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "ONE two" { t.Fatalf("expected only survivor applied, got %q", out.FinalTranscript.Segments[0].Text) } } func TestRunnerValidatorCardinalityErrorStopsPipelineWithPartialProgress(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "teh cat"}}} good := fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }} badValidator := fakeValidator{name: "bad", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { // Missing one decision triggers cardinality error. return validators.Result{ValidatorName: "bad", Decisions: nil}, nil }} bad := fakeModule{key: "m2", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{badValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: 1}}, nil }} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{"m1": good, "m2": bad}}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m1", InstanceName: "m1"}, {ModuleKey: "m2", InstanceName: "m2"}}}) if err == nil { t.Fatal("expected cardinality error") } if out.FinalTranscript.Segments[0].Text != "the cat" { t.Fatalf("expected partial progress preserved, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults) != 2 || out.ModuleResults[1].Status != ModuleStatusFailed { t.Fatalf("expected second module failed") } } func TestRunnerApplicationSkipAfterValidatorApprovalReported(t *testing.T) { transcript := &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "word word"}}} allow := fakeValidator{name: "allow", validateF: func(req contracts.ValidationRequest) (validators.Result, error) { return validators.Result{ValidatorName: "allow", Decisions: []validators.Decision{{ProposalIndex: req.CandidateProposal[0].ProposalIndex, Approved: true, ReasonCode: validators.ReasonApproved, Message: "ok"}}}, nil }} r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{allow}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "word", CorrectedText: "term", Confidence: 1}}, nil }}, }}) out, err := r.Run(context.Background(), RunInput{Transcript: transcript, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}}) if err != nil { t.Fatalf("Run error: %v", err) } if len(out.ModuleResults[0].SkippedChanges) != 1 { t.Fatalf("expected application skip recorded") } if len(out.ModuleResults[0].ValidatorRejected) != 0 { t.Fatalf("expected no validator rejection") } } func ptrConfig(c config.Config) *config.Config { return &c } type fakeStructuredClient struct { responses []validators.LLMValidationResponse err error calls int } func (f *fakeStructuredClient) CompleteStructured(ctx context.Context, req contracts.StructuredCompletionRequest, out any) (contracts.StructuredCompletionResponse, error) { _ = ctx _ = req f.calls++ if f.err != nil { return contracts.StructuredCompletionResponse{}, f.err } if len(f.responses) == 0 { return contracts.StructuredCompletionResponse{}, errors.New("unexpected call") } resp := f.responses[0] f.responses = f.responses[1:] target := out.(*validators.LLMValidationResponse) *target = resp return contracts.StructuredCompletionResponse{}, nil } type countingScheduler struct{ runs int } func (s *countingScheduler) Run(ctx context.Context, fn func(context.Context) error) error { s.runs++ return fn(ctx) } type lenEstimator struct{} func (lenEstimator) EstimateTokens(text string) int { return len(text) } func TestRunnerLLMValidatorApprovalApplied(t *testing.T) { client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{{Validations: []validators.LLMValidationDecision{{CorrectionIndex: 0, Approved: true, Confidence: 0.9, Reason: "ok"}}}}} llmValidator, err := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") if err != nil { t.Fatalf("NewLLMBackedValidator: %v", err) } r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 1}}, nil }}, }}) cfg := config.Default() out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "There were gestures at the temple."}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMClient: client, }) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "There were Jesters at the temple." { t.Fatalf("expected applied LLM-approved proposal, got %q", out.FinalTranscript.Segments[0].Text) } } func TestRunnerLLMValidatorRejectionPreventsApplication(t *testing.T) { client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{{Validations: []validators.LLMValidationDecision{{CorrectionIndex: 0, Approved: false, Confidence: 0.95, Reason: "reject"}}}}} llmValidator, _ := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 1}}, nil }}, }}) cfg := config.Default() out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "There were gestures at the temple."}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMClient: client, }) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "There were gestures at the temple." { t.Fatalf("expected rejected proposal not applied") } if len(out.ModuleResults[0].ValidatorRejected) != 1 { t.Fatalf("expected validator rejection record") } } func TestRunnerLLMValidatorMalformedResponseRejectsBatchAndKeepsPartialProgress(t *testing.T) { client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{{Validations: []validators.LLMValidationDecision{{CorrectionIndex: 99, Approved: true, Confidence: 0.9, Reason: "bad index"}}}}} llmValidator, _ := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m1": fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 1}}, nil }}, "m2": fakeModule{key: "m2", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: 1}}, nil }}, }}) cfg := config.Default() out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "teh cat"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m1", InstanceName: "m1"}, {ModuleKey: "m2", InstanceName: "m2"}}, ValidationLLMClient: client, }) if err != nil { t.Fatalf("expected malformed validator response to downgrade, got %v", err) } if out.FinalTranscript.Segments[0].Text != "the cat" { t.Fatalf("expected partial progress retained") } if len(out.ModuleResults) != 2 || len(out.ModuleResults[1].ValidatorRejected) != 1 { t.Fatalf("expected second module rejection, got %+v", out.ModuleResults) } if len(out.ModuleResults[1].Warnings) != 1 || out.ModuleResults[1].Warnings[0].ReasonCode != validators.ReasonValidatorMalformed { t.Fatalf("expected malformed warning, got %+v", out.ModuleResults[1].Warnings) } } func TestRunnerLLMValidatorMissingDecisionRejectsBatch(t *testing.T) { client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{{Validations: []validators.LLMValidationDecision{}}}} llmValidator, _ := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 1}}, nil }}, }}) cfg := config.Default() _, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "There were gestures at the temple."}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMClient: client, }) if err != nil { t.Fatalf("expected missing decision downgrade, got %v", err) } } func TestRunnerLLMValidatorDuplicateDecisionRejectsBatch(t *testing.T) { client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{{Validations: []validators.LLMValidationDecision{ {CorrectionIndex: 0, Approved: true, Confidence: 0.9, Reason: "ok"}, {CorrectionIndex: 0, Approved: false, Confidence: 0.9, Reason: "dup"}, }}}} llmValidator, _ := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 1}}, nil }}, }}) cfg := config.Default() _, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "There were gestures at the temple."}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMClient: client, }) if err != nil { t.Fatalf("expected duplicate decision downgrade, got %v", err) } } func TestRunnerLLMValidatorBatchingAndSchedulerUsage(t *testing.T) { client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{ {Validations: []validators.LLMValidationDecision{{CorrectionIndex: 0, Approved: true, Confidence: 0.9, Reason: "ok"}}}, {Validations: []validators.LLMValidationDecision{{CorrectionIndex: 1, Approved: true, Confidence: 0.9, Reason: "ok"}}}, }} llmValidator, _ := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") llmValidator.SetTokenEstimator(lenEstimator{}) scheduler := &countingScheduler{} cfg := config.Default() cfg.ValidationMaxPromptTokens = 260 r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyReplaceAll, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{ {TargetSegmentID: 1, OriginalText: "alpha", CorrectedText: strings.Repeat("B", 40), Confidence: 1}, {TargetSegmentID: 1, OriginalText: "gamma", CorrectedText: strings.Repeat("D", 40), Confidence: 1}, }, nil }}, }}) out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "alpha gamma"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMClient: client, ValidationLLMScheduler: scheduler, }) if err != nil { t.Fatalf("unexpected run error: %v", err) } if scheduler.runs < 2 { t.Fatalf("expected scheduler to run per batch, got %d", scheduler.runs) } if client.calls < 2 { t.Fatalf("expected multiple llm calls for batching, got %d", client.calls) } if len(out.ModuleResults[0].AppliedChanges) != 2 { t.Fatalf("expected both approved proposals applied") } } func TestRunnerLLMValidatorDiagnosticsWrittenAndRedacted(t *testing.T) { primarySecret := "runner-primary-secret" validationSecret := "runner-validation-secret" client := &fakeStructuredClient{responses: []validators.LLMValidationResponse{{Validations: []validators.LLMValidationDecision{{CorrectionIndex: 0, Approved: true, Confidence: 0.9, Reason: validationSecret}}}}} llmValidator, _ := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "") cfg := config.Default() cfg.PrimaryLLM.APIKey = primarySecret cfg.ValidationLLM.APIKey = validationSecret diagDir := t.TempDir() r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) { return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 1}}, nil }}, }}) out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "There were gestures at the temple. " + primarySecret}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ValidationLLMClient: client, ValidationDiagnosticsDir: diagDir, }) if err != nil { t.Fatalf("Run error: %v", err) } if len(out.ModuleResults[0].ValidatorDecisions) == 0 || out.ModuleResults[0].ValidatorDecisions[0].DiagnosticArtifactPath == "" { t.Fatalf("expected diagnostic artifact path on decision") } matches, globErr := filepath.Glob(filepath.Join(diagDir, "m", "*.json")) if globErr != nil { t.Fatalf("glob diagnostics: %v", globErr) } if len(matches) == 0 { t.Fatalf("expected diagnostics JSON artifacts under %s", filepath.Join(diagDir, "m")) } for _, path := range matches { raw, readErr := os.ReadFile(path) if readErr != nil { t.Fatalf("read diagnostic %q: %v", path, readErr) } if strings.Contains(string(raw), primarySecret) || strings.Contains(string(raw), validationSecret) { t.Fatalf("configured secret leaked in diagnostics %q: %s", path, string(raw)) } } raw, readErr := os.ReadFile(out.ModuleResults[0].ValidatorDecisions[0].DiagnosticArtifactPath) if readErr != nil { t.Fatalf("read decision diagnostic: %v", readErr) } if !strings.Contains(string(raw), "[REDACTED]") { t.Fatalf("expected redaction marker in diagnostics") } } func TestRunnerAcceptsLLMSchedulerType(t *testing.T) { s, err := llm.NewScheduler(1) if err != nil { t.Fatalf("NewScheduler: %v", err) } if s == nil { t.Fatal("expected scheduler instance") } } type proposalGenerationModule struct { key string policy proposals.ReplacementPolicy } func (m proposalGenerationModule) Key() string { return m.key } func (m proposalGenerationModule) ReplacementPolicy() proposals.ReplacementPolicy { return m.policy } func (m proposalGenerationModule) Validators() []contracts.Validator { return nil } func (m proposalGenerationModule) Propose(ctx context.Context, req contracts.ProposalRequest) (contracts.ProposalResult, error) { result, err := proposal_generation.GenerateCandidates(ctx, proposal_generation.Request{ ModuleKey: req.RunSpec.ModuleKey, ModuleInstance: req.RunSpec.InstanceName, ReplacementPolicy: req.RunSpec.ReplacementPolicy, WorkingTranscript: req.WorkingTranscript, Config: req.Config, Glossary: req.Glossary, Messages: []contracts.LLMMessage{ {Role: "system", Content: "return transcript corrections"}, {Role: "user", Content: "produce one safe correction"}, }, LLMClient: req.LLMClient, Scheduler: req.LLMScheduler, DiagnosticsDir: req.DiagnosticsDir, }) if err != nil { return contracts.ProposalResult{}, err } return contracts.ProposalResult{Proposals: result.Corrections, Warnings: result.Warnings}, nil } type fakeProposalStructuredClient struct { responses []proposal_generation.StructuredCorrectionSet } func (f *fakeProposalStructuredClient) CompleteStructured(ctx context.Context, req contracts.StructuredCompletionRequest, out any) (contracts.StructuredCompletionResponse, error) { _ = ctx _ = req if len(f.responses) == 0 { return contracts.StructuredCompletionResponse{}, errors.New("unexpected call") } target, ok := out.(*proposal_generation.StructuredCorrectionSet) if !ok { return contracts.StructuredCompletionResponse{}, errors.New("unexpected output type") } *target = f.responses[0] f.responses = f.responses[1:] return contracts.StructuredCompletionResponse{}, nil } func TestRunnerProposalGenerationHelperFlowsThroughPipeline(t *testing.T) { client := &fakeProposalStructuredClient{ responses: []proposal_generation.StructuredCorrectionSet{ { Corrections: []proposal_generation.StructuredCorrectionProposal{ {TargetSegmentID: 1, OriginalText: "teh", CorrectedText: "the", Confidence: 0.9}, }, }, }, } scheduler := &countingScheduler{} cfg := config.Default() diagDir := t.TempDir() r := New(fakeFactory{modules: map[string]contracts.TranscriptModule{ "m": proposalGenerationModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique}, }}) out, err := r.Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "teh cat"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "m", InstanceName: "m"}}, ProposalLLMClient: client, ProposalLLMScheduler: scheduler, ProposalDiagnosticsDir: diagDir, }) if err != nil { t.Fatalf("unexpected run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "the cat" { t.Fatalf("expected proposal-generated correction to apply, got %q", out.FinalTranscript.Segments[0].Text) } if scheduler.runs != 1 { t.Fatalf("expected proposal scheduler use, got %d runs", scheduler.runs) } if len(out.ModuleResults) != 1 || len(out.ModuleResults[0].AppliedChanges) != 1 { t.Fatalf("expected one applied change, got %+v", out.ModuleResults) } matches, globErr := filepath.Glob(filepath.Join(diagDir, "m", "*proposal-generation*response-payload.json")) if globErr != nil { t.Fatalf("glob diagnostics: %v", globErr) } if len(matches) == 0 { t.Fatalf("expected proposal-generation diagnostics artifacts in %s", filepath.Join(diagDir, "m")) } } func TestRunnerGrammarModuleUsesConfidenceThreshold(t *testing.T) { client := &fakeProposalStructuredClient{ responses: []proposal_generation.StructuredCorrectionSet{ { Corrections: []proposal_generation.StructuredCorrectionProposal{ {TargetSegmentID: 1, OriginalText: "hello ,world", CorrectedText: "Hello, world", Confidence: 0.5}, }, }, }, } cfg := config.Default() cfg.Thresholds.Grammar = 0.9 factory := modules.NewFactory(modules.Dependencies{}) out, err := New(factory).Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "hello ,world"}}}, Glossary: &schema.Glossary{}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar"}}, ProposalLLMClient: client, }) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "hello ,world" { t.Fatalf("expected no changes due to confidence threshold, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults) != 1 || len(out.ModuleResults[0].ValidatorRejected) == 0 { t.Fatalf("expected validator rejection, got %+v", out.ModuleResults) } if out.ModuleResults[0].ValidatorRejected[0].ReasonCode != validators.ReasonLowConfidence { t.Fatalf("expected low confidence reason, got %+v", out.ModuleResults[0].ValidatorRejected[0]) } } func TestRunnerGlossaryModuleUsesConfidenceThreshold(t *testing.T) { client := &fakeProposalStructuredClient{ responses: []proposal_generation.StructuredCorrectionSet{ { Corrections: []proposal_generation.StructuredCorrectionProposal{ {TargetSegmentID: 1, OriginalText: "gestures", CorrectedText: "Jesters", Confidence: 0.5}, }, }, }, } cfg := config.Default() cfg.Thresholds.Glossary = 0.9 factory := modules.NewFactory(modules.Dependencies{}) out, err := New(factory).Run(context.Background(), RunInput{ Config: &cfg, Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "There were gestures"}}}, Glossary: &schema.Glossary{Entries: []schema.GlossaryEntry{{Name: "Jesters", Category: "faction", Summary: "Faction"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "glossary", InstanceName: "glossary"}}, ProposalLLMClient: client, }) if err != nil { t.Fatalf("Run error: %v", err) } if out.FinalTranscript.Segments[0].Text != "There were gestures" { t.Fatalf("expected no changes due to glossary confidence threshold, got %q", out.FinalTranscript.Segments[0].Text) } if len(out.ModuleResults) != 1 || len(out.ModuleResults[0].ValidatorRejected) == 0 { t.Fatalf("expected validator rejection, got %+v", out.ModuleResults) } if out.ModuleResults[0].ValidatorRejected[0].ReasonCode != validators.ReasonLowConfidence { t.Fatalf("expected low confidence reason, got %+v", out.ModuleResults[0].ValidatorRejected[0]) } }