Index applied changes for repair outcomes
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
**Updated:** 2026-11-11
|
||||
|
||||
**Status:** 5 of nine findings resolved. Findings refer to implementation revision
|
||||
**Status:** 6 of nine findings resolved. Findings refer to implementation revision
|
||||
`aa8073c`. See the [implementation plan](implementation.md).
|
||||
|
||||
## Review Result
|
||||
@@ -216,6 +216,8 @@ filtering evidence for candidates that remain excluded.
|
||||
|
||||
### R6 — Index Application Results for Repair Outcomes (P3)
|
||||
|
||||
**Completed.** Application outcomes use a stage-local index built once. The runner test covers an original approval, a skipped revision, and an applied revision in the same stage.
|
||||
|
||||
**Affected plan:** Stage 6, item 5, and Stage 7, item 3.
|
||||
|
||||
**Evidence:** After applying the stage, [Runner.Run](../../internal/framework/runner/runner.go#L244)
|
||||
|
||||
@@ -114,22 +114,29 @@ type repairModule struct{ outcomeModule }
|
||||
func (m repairModule) RepairPromptID() string { return "repair" }
|
||||
|
||||
func TestRunnerAppliesRevisionsInOriginalOrder(t *testing.T) {
|
||||
sectionIndex := 0
|
||||
candidates := []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: .9}, {TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "fox", Confidence: .1}}
|
||||
client := &repairClient{outputs: []string{`{"proposal_ref":2,"action":"revise","correction":{"id":1,"original_text":"cat","corrected_text":"fox","confidence":0.9}}`}}
|
||||
candidates := []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: .9}, {TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "fox", Confidence: .1}, {TargetSegmentID: 1, OriginalText: "bird", CorrectedText: "owl", Confidence: .1}}
|
||||
client := &repairClient{outputs: []string{`{"proposal_ref":2,"action":"revise","correction":{"id":1,"original_text":"cat","corrected_text":"fox","confidence":0.9}}`,
|
||||
`{"proposal_ref":3,"action":"revise","correction":{"id":1,"original_text":"bird","corrected_text":"owl","confidence":0.9}}`,
|
||||
}}
|
||||
module := repairModule{outcomeModule{fakeModule: fakeModule{key: "grammar", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{validators.ProposalShapeValidator{}, validators.NoEffectValidator{}, validators.OriginalTextPresenceValidator{}, validators.ConfidenceThresholdValidator{}}}, proposeResult: func(req contracts.ProposalRequest) (contracts.ProposalResult, error) {
|
||||
return contracts.ProposalResult{Proposals: candidates, SegmentReferences: []int{1, 1}, Exchange: &structuredoutput.Exchange{PromptID: "proposal"}}, nil
|
||||
return contracts.ProposalResult{Proposals: candidates, SegmentReferences: []int{1, 1, 1}, Exchange: &structuredoutput.Exchange{PromptID: "proposal"}}, nil
|
||||
}}}
|
||||
runner := New(fakeFactory{modules: map[string]contracts.TranscriptModule{"grammar": module}})
|
||||
out, err := runner.Run(context.Background(), RunInput{Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "cat"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar", RepairClient: client, Settings: config.Stage{ConfidenceThreshold: .8, LLM: config.LLM{CorrectionRepairAttempts: 1}}}}})
|
||||
out, err := runner.Run(context.Background(), RunInput{Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "cat and bird"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar", RepairClient: client, Settings: config.Stage{ConfidenceThreshold: .8, LLM: config.LLM{CorrectionRepairAttempts: 1}}}}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out.FinalTranscript.Segments[0].Text != "dog" || len(out.WorkUnits) != 1 || len(out.WorkUnits[0].Repairs) != 1 || out.WorkUnits[0].Repairs[0].Outcome != "approved_skipped" || out.WorkUnits[0].CorrectionsApplied != 1 || len(out.ModuleResults[0].SkippedChanges) != 1 || out.ModuleResults[0].SkippedChanges[0].ProposalIndex != 1 {
|
||||
t.Fatalf("output=%+v section=%d", out, sectionIndex)
|
||||
if out.FinalTranscript.Segments[0].Text != "dog and owl" || len(out.WorkUnits) != 1 || len(out.WorkUnits[0].Repairs) != 2 || out.WorkUnits[0].Repairs[0].Outcome != "approved_skipped" || out.WorkUnits[0].Repairs[1].Outcome != "applied" || out.WorkUnits[0].CorrectionsApplied != 2 || len(out.ModuleResults[0].SkippedChanges) != 1 || out.ModuleResults[0].SkippedChanges[0].ProposalIndex != 1 {
|
||||
t.Fatalf("output=%+v", out)
|
||||
}
|
||||
if len(out.WorkUnits[0].Operations) != 1 || out.WorkUnits[0].Operations[0].Reference.Kind != outcomes.OperationCorrectionRepair || len(out.WorkUnits[0].Operations[0].RecoveryReasons) != 1 || out.WorkUnits[0].Operations[0].RecoveryReasons[0] != outcomes.RecoveryCorrectionRevised {
|
||||
t.Fatalf("repair operations: %+v", out.WorkUnits[0].Operations)
|
||||
operations := out.WorkUnits[0].Operations
|
||||
if len(operations) != 2 {
|
||||
t.Fatalf("repair operations: %+v", operations)
|
||||
}
|
||||
for _, op := range operations {
|
||||
if op.Reference.Kind != outcomes.OperationCorrectionRepair || len(op.RecoveryReasons) != 1 || op.RecoveryReasons[0] != outcomes.RecoveryCorrectionRevised {
|
||||
t.Fatalf("repair operation: %+v", op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
||||
unit.State = outcomes.WorkCompleted
|
||||
unit.CorrectionsApplied = appliedBySection[sectionPos]
|
||||
}
|
||||
var appliedIndices map[int]struct{}
|
||||
for i := range workUnits[moduleIndex*len(sections) : (moduleIndex+1)*len(sections)] {
|
||||
unit := &workUnits[moduleIndex*len(sections)+i]
|
||||
for j := range unit.Repairs {
|
||||
@@ -248,13 +249,16 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
||||
if branch.Outcome != "approved" {
|
||||
continue
|
||||
}
|
||||
branch.Outcome = "approved_skipped"
|
||||
for _, change := range applied {
|
||||
if change.ProposalIndex == branch.ProposalIndex {
|
||||
branch.Outcome = "applied"
|
||||
break
|
||||
if appliedIndices == nil {
|
||||
appliedIndices = make(map[int]struct{}, len(applied))
|
||||
for _, change := range applied {
|
||||
appliedIndices[change.ProposalIndex] = struct{}{}
|
||||
}
|
||||
}
|
||||
branch.Outcome = "approved_skipped"
|
||||
if _, ok := appliedIndices[branch.ProposalIndex]; ok {
|
||||
branch.Outcome = "applied"
|
||||
}
|
||||
}
|
||||
}
|
||||
attributeApplyFiltering(sections, pipelineResult.Approved, skipped, policy, workUnits[moduleIndex*len(sections):(moduleIndex+1)*len(sections)])
|
||||
|
||||
Reference in New Issue
Block a user