Derive independent glossary context and protection
This commit is contained in:
@@ -43,6 +43,7 @@ func ApplyProposals(
|
||||
transcript *schema.Transcript,
|
||||
proposals []EnrichedCorrectionProposal,
|
||||
policy ReplacementPolicy,
|
||||
allowsChange func(before, after string) bool,
|
||||
) ApplyResult {
|
||||
working := cloneTranscript(transcript)
|
||||
ordered := sortProposalsByIndex(proposals)
|
||||
@@ -68,6 +69,11 @@ func ApplyProposals(
|
||||
continue
|
||||
}
|
||||
|
||||
if allowsChange != nil && !allowsChange(working.Segments[segmentIdx].Text, preview.CorrectedSegmentText) {
|
||||
skipped = append(skipped, newSkippedChange(proposal, SkipReasonProtectedTerm))
|
||||
continue
|
||||
}
|
||||
|
||||
working.Segments[segmentIdx].Text = preview.CorrectedSegmentText
|
||||
applied = append(applied, AppliedChange{
|
||||
ProposalIndex: proposal.ProposalIndex,
|
||||
@@ -149,6 +155,8 @@ func skipReasonMessage(reason ProposalSkipReason) string {
|
||||
return "original_text and corrected_text are identical"
|
||||
case SkipReasonEmptyResultingText:
|
||||
return "proposal would leave the segment empty"
|
||||
case SkipReasonProtectedTerm:
|
||||
return "proposal may alter protected glossary terminology"
|
||||
case SkipReasonInvalidProposal:
|
||||
return "proposal failed structural or policy validation"
|
||||
default:
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestApplyProposalsFixtureTranscriptGoldens(t *testing.T) {
|
||||
t.Fatalf("failed to parse fixture policy: %v", err)
|
||||
}
|
||||
|
||||
result := ApplyProposals(&fixture.Transcript, fixture.Proposals, policy)
|
||||
result := ApplyProposals(&fixture.Transcript, fixture.Proposals, policy, nil)
|
||||
assertJSONSemanticallyEqualFromGolden(t, result.Transcript, name+".transcript.golden.json")
|
||||
})
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestApplyProposalsFixtureSkipCoverage(t *testing.T) {
|
||||
t.Fatalf("failed to parse fixture policy: %v", err)
|
||||
}
|
||||
|
||||
result := ApplyProposals(&fixture.Transcript, fixture.Proposals, policy)
|
||||
result := ApplyProposals(&fixture.Transcript, fixture.Proposals, policy, nil)
|
||||
if len(result.Skipped) == 0 {
|
||||
t.Fatal("expected at least one skipped change")
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func TestApplyProposalsFixtureMixedScenarioRecordGoldens(t *testing.T) {
|
||||
t.Fatalf("failed to parse fixture policy: %v", err)
|
||||
}
|
||||
|
||||
result := ApplyProposals(&fixture.Transcript, fixture.Proposals, policy)
|
||||
result := ApplyProposals(&fixture.Transcript, fixture.Proposals, policy, nil)
|
||||
payload := recordsPayload{
|
||||
AppliedChanges: result.Applied,
|
||||
SkippedChanges: result.Skipped,
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestApplyProposalsApplyingOneProposal(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
|
||||
if got := result.Transcript.Segments[0].Text; got != "Jesters" {
|
||||
t.Fatalf("expected corrected text Jesters, got %q", got)
|
||||
@@ -57,7 +57,7 @@ func TestApplyProposalsApplyingMultipleProposalsInStableOrder(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyReplaceAll)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyReplaceAll, nil)
|
||||
|
||||
if got := result.Transcript.Segments[0].Text; got != "X X" {
|
||||
t.Fatalf("expected final text X X, got %q", got)
|
||||
@@ -93,7 +93,7 @@ func TestApplyProposalsStaleProposalSkippedAfterEarlierChange(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
|
||||
if got := result.Transcript.Segments[0].Text; got != "foo-bar" {
|
||||
t.Fatalf("expected final text foo-bar, got %q", got)
|
||||
@@ -120,7 +120,7 @@ func TestApplyProposalsRechecksBlankResultAfterEarlierDeletion(t *testing.T) {
|
||||
t.Fatalf("proposal must be initially applicable: %+v", preview)
|
||||
}
|
||||
}
|
||||
result := ApplyProposals(transcript, proposed, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposed, ReplacementPolicyRequireUnique, nil)
|
||||
if got := result.Transcript.Segments[0].Text; got != " b" {
|
||||
t.Fatalf("expected literal surviving text, got %q", got)
|
||||
}
|
||||
@@ -143,7 +143,7 @@ func TestApplyProposalsMissingSegmentSkip(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
|
||||
if len(result.Skipped) != 1 {
|
||||
t.Fatalf("expected 1 skipped change, got %d", len(result.Skipped))
|
||||
@@ -167,7 +167,7 @@ func TestApplyProposalsRequireUniqueAmbiguousSkip(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
|
||||
if len(result.Skipped) != 1 {
|
||||
t.Fatalf("expected 1 skipped change, got %d", len(result.Skipped))
|
||||
@@ -191,7 +191,7 @@ func TestApplyProposalsReplaceAllMultipleReplacements(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyReplaceAll)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyReplaceAll, nil)
|
||||
|
||||
if len(result.Applied) != 1 {
|
||||
t.Fatalf("expected 1 applied change, got %d", len(result.Applied))
|
||||
@@ -220,7 +220,7 @@ func TestApplyProposalsNoMutationOfInputTranscript(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
_ = ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
_ = ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
|
||||
if !reflect.DeepEqual(transcript, before) {
|
||||
t.Fatalf("input transcript mutated: before=%+v after=%+v", before, transcript)
|
||||
@@ -249,7 +249,7 @@ func TestApplyProposalsSegmentMetadataPreservation(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
seg := result.Transcript.Segments[0]
|
||||
|
||||
if seg.Speaker != "A" || seg.Start != 12.5 || seg.End != 15.0 {
|
||||
@@ -283,7 +283,7 @@ func TestApplyProposalsAppliedAndSkippedRecords(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique)
|
||||
result := ApplyProposals(transcript, proposals, ReplacementPolicyRequireUnique, nil)
|
||||
|
||||
if len(result.Applied) != 1 || len(result.Skipped) != 1 {
|
||||
t.Fatalf("expected 1 applied and 1 skipped, got applied=%d skipped=%d", len(result.Applied), len(result.Skipped))
|
||||
|
||||
@@ -17,6 +17,7 @@ const (
|
||||
SkipReasonNoEffect ProposalSkipReason = "no_effect"
|
||||
SkipReasonEmptyResultingText ProposalSkipReason = "empty_resulting_segment"
|
||||
SkipReasonInvalidProposal ProposalSkipReason = "invalid_proposal"
|
||||
SkipReasonProtectedTerm ProposalSkipReason = "protected_glossary_term"
|
||||
)
|
||||
|
||||
// SegmentPreviewResult is a deterministic preview of applying one proposal to
|
||||
|
||||
Reference in New Issue
Block a user