Strengthen validation retries and checkpoint safety

This commit is contained in:
2026-08-27 11:50:37 +00:00
parent b2e83bd6e7
commit e6b2ae88d2
73 changed files with 934 additions and 1560 deletions

View File

@@ -18,7 +18,7 @@ const (
validationSkipped validationOutcome = "skipped"
)
const defaultCorrectionGuidance = "Correct the candidate to satisfy the validator requirements."
const correctionRequestIntroduction = "The previous response failed semantic validation. Return one complete corrected replacement response, not a patch, explanation, or commentary.\n\nCorrect all of the following:\n"
// validationRecord captures the settled result of one configured validator.
// Its fields remain private so reports cannot expose mutable warning storage.
@@ -82,33 +82,39 @@ func (report validationReport) FirstFailure() *validationRecord {
return nil
}
func (report validationReport) CorrectionGuidance() string {
func (report validationReport) CorrectionRequest() (string, error) {
seen := make(map[string]struct{})
parts := make([]string, 0, len(report.records))
used := 0
for _, record := range report.records {
if record.outcome != validationRejected {
continue
}
guidance := strings.TrimSpace(record.correctionGuidance)
if guidance == "" {
guidance = defaultCorrectionGuidance
return "", fmt.Errorf("validator %q rejected output without correction guidance", record.validatorName)
}
if _, exists := seen[guidance]; exists {
continue
}
separator := 0
if len(parts) > 0 {
separator = 1
}
if used+separator+len(guidance) > contracts.MaxCorrectionGuidanceBytes {
break
}
seen[guidance] = struct{}{}
parts = append(parts, guidance)
used += separator + len(guidance)
}
return strings.Join(parts, "\n")
if len(parts) == 0 {
return "", errors.New("validation report contains no correction guidance")
}
var builder strings.Builder
builder.WriteString(correctionRequestIntroduction)
for index, guidance := range parts {
fmt.Fprintf(&builder, "%d. %s", index+1, guidance)
if index+1 < len(parts) {
builder.WriteByte('\n')
}
}
request := builder.String()
if len(request) > contracts.MaxCorrectionGuidanceBytes {
return "", fmt.Errorf("aggregate correction request exceeds maximum length of %d bytes", contracts.MaxCorrectionGuidanceBytes)
}
return request, nil
}
type validationInvocation struct {
@@ -184,15 +190,11 @@ func executeValidationChain(ctx context.Context, chain preparedValidatorChain, i
report.records = append(report.records, validationRecord{validatorName: binding.Module, outcome: validationApproved, attemptCount: attempt, warnings: cloneWarnings(invocation.result.Warnings), diagnosticPath: invocation.result.DiagnosticArtifactPath})
break
}
reason := invocation.result.ReasonCode
if reason == "" {
reason = "output_rejected"
}
message := invocation.result.Message
if message == "" {
message = "output rejected"
}
report.records = append(report.records, validationRecord{validatorName: binding.Module, outcome: validationRejected, attemptCount: attempt, reasonCode: reason, message: message, diagnosticPath: invocation.result.DiagnosticArtifactPath, warnings: cloneWarnings(invocation.result.Warnings), correctionGuidance: invocation.result.CorrectionGuidance})
report.records = append(report.records, validationRecord{validatorName: binding.Module, outcome: validationRejected, attemptCount: attempt, reasonCode: invocation.result.ReasonCode, message: message, diagnosticPath: invocation.result.DiagnosticArtifactPath, warnings: cloneWarnings(invocation.result.Warnings), correctionGuidance: invocation.result.CorrectionGuidance})
break
}
}