Index initial repair findings and compact filtering once

This commit is contained in:
2026-11-11 12:28:42 +00:00
parent 5d90478f70
commit 8c93f728c0
2 changed files with 36 additions and 23 deletions

View File

@@ -2,7 +2,7 @@
**Updated:** 2026-11-11
**Status:** 4 of nine findings resolved. Findings refer to implementation revision
**Status:** 5 of nine findings resolved. Findings refer to implementation revision
`aa8073c`. See the [implementation plan](implementation.md).
## Review Result
@@ -184,6 +184,8 @@ scanning and allocation in the feedback path.
### R5 — Index Initial Findings and Compact Filtering Once (P3)
**Completed.** Initial findings are grouped by proposal in validator order. Superseded filtering records are removed once, including on partial returns. Runner and report tests pass.
**Affected plan:** Stage 6, items 1 and 5.
**Evidence:** [repairCandidates](../../internal/framework/runner/repair.go#L59)

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"path/filepath"
"slices"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/redaction"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/contracts"
@@ -61,29 +62,44 @@ func repairCandidates(ctx context.Context, input validateSectionCandidatesInput,
if input.Spec.Settings.LLM.CorrectionRepairAttempts <= 0 {
return initial, nil, nil
}
initialFindings := make(map[int][]ValidatorRejectedChange)
for _, decision := range initial.rejected {
initialFindings[decision.ProposalIndex] = append(initialFindings[decision.ProposalIndex], decision)
}
superseded := make(map[int]bool)
var branches []RepairOutcome
finish := func(err error) (validateSectionCandidatesResult, []RepairOutcome, error) {
if len(superseded) > 0 {
initial.filtered = slices.DeleteFunc(initial.filtered, func(item outcomes.FilteredProposal) bool {
return superseded[item.ProposalIndex]
})
}
return initial, branches, err
}
for position, original := range input.SectionEnriched {
if !actionable(original.ProposalIndex, initial.rejected) {
rejected := initialFindings[original.ProposalIndex]
if !actionable(original.ProposalIndex, rejected) {
continue
}
branch := RepairOutcome{ProposalIndex: original.ProposalIndex, Original: original, Final: original, Outcome: "interrupted"}
if err := ctx.Err(); err != nil {
branches = append(branches, branch)
return initial, branches, err
return finish(err)
}
if input.Spec.RepairClient == nil || input.Exchange == nil || promptID == "" {
branches = append(branches, branch)
return initial, branches, fmt.Errorf("correction repair setup is unavailable")
return finish(fmt.Errorf("correction repair setup is unavailable"))
}
localRef := 0
if position < len(references) {
localRef = references[position]
} else if input.WorkingTranscript != nil {
for i, s := range input.WorkingTranscript.Segments {
if s.ID == original.TargetSegmentID {
localRef = i + 1
break
}
} else {
if input.Section == nil {
input.Section = validators.NewSectionLookup(input.WorkingTranscript)
}
if pos, ok := input.Section.Position(original.TargetSegmentID); ok {
localRef = pos + 1
}
}
current := original
@@ -95,13 +111,13 @@ func repairCandidates(ctx context.Context, input validateSectionCandidatesInput,
}
return findings
}
findings := safeFindings(initial.rejected)
findings := safeFindings(rejected)
replay := &structuredoutput.Replay{Exchange: input.Exchange}
for attempt := 0; attempt < input.Spec.Settings.LLM.CorrectionRepairAttempts; attempt++ {
payload, err := (proposal_generation.RepairRequest{ProposalRef: position + 1, Correction: proposal_generation.StructuredCorrectionProposal{SegmentReference: localRef, OriginalText: current.OriginalText, CorrectedText: current.CorrectedText, Confidence: current.Confidence}, Findings: findings}).JSON()
if err != nil {
branches = append(branches, branch)
return initial, branches, err
return finish(err)
}
var response proposal_generation.RepairResponse
proposalIndex, attemptNumber := original.ProposalIndex, attempt+1
@@ -145,7 +161,7 @@ func repairCandidates(ctx context.Context, input validateSectionCandidatesInput,
break
}
branches = append(branches, branch)
return initial, branches, callErr
return finish(callErr)
}
decoded, err := proposal_generation.DecodeRepairResponse(completion.Content, position+1)
if err != nil {
@@ -197,26 +213,21 @@ func repairCandidates(ctx context.Context, input validateSectionCandidatesInput,
initial.operations = append(initial.operations, validated.operations...)
if validationErr != nil {
branches = append(branches, branch)
return initial, branches, validationErr
return finish(validationErr)
}
if len(validated.approved) == 1 {
branch.Attempts[len(branch.Attempts)-1].Approved = true
branch.Outcome = "approved"
initial.approved = append(initial.approved, current)
retained := initial.filtered[:0]
for _, item := range initial.filtered {
if item.ProposalIndex != original.ProposalIndex {
retained = append(retained, item)
}
}
initial.filtered = retained
superseded[original.ProposalIndex] = true
break
}
findings = safeFindings(validated.rejected)
if actionable(original.ProposalIndex, validated.rejected) {
if attempt+1 == input.Spec.Settings.LLM.CorrectionRepairAttempts {
branch.Outcome = "exhausted"
break
}
findings = safeFindings(validated.rejected)
continue
}
branch.Outcome = "unavailable"
@@ -230,7 +241,7 @@ func repairCandidates(ctx context.Context, input validateSectionCandidatesInput,
}
branches = append(branches, branch)
}
return initial, branches, nil
return finish(nil)
}
func repairOperations(input validateSectionCandidatesInput, branches []RepairOutcome) []outcomes.Operation {