Skip to content

feat: repair-oriented structured-output recovery (ADR-0031)#23

Merged
muhammetsafak merged 1 commit into
mainfrom
feat/repair-oriented-recovery
Jul 4, 2026
Merged

feat: repair-oriented structured-output recovery (ADR-0031)#23
muhammetsafak merged 1 commit into
mainfrom
feat/repair-oriented-recovery

Conversation

@muhammetsafak

Copy link
Copy Markdown
Member

Closes #22.

Replaces the blind byte-identical structured-output retry with a recovery ladder: fence salvage → classify → repair-oriented retry → degrade.

Changes

  • Phase 0 — fence salvage (internal/render/findings.go): ParseFindings unwraps a lone ```json … ``` fence pair before json.Unmarshal. Non-fenced input is byte-identical, so the cache key is unchanged and cached FormatJSON fenced bodies render as findings on replay.
  • Phase 1+2 — classify + branched repair: ParseFindings returns a typed *ParseError{Kind} (empty / prose / malformed-json / schema) with unchanged message text (existing substring assertions hold). tryStructuredReview classifies the failure and sends a failure-mode-specific repair prompt instead of the identical request — a hard "JSON only" reset for prose/schema-ignored, or a "complete the JSON" nudge (partial output embedded, max_tokens raised) for a truncated attempt. The retry stays a fresh single-shot request; the markdown-fallback terminal state and summed usage are unchanged.
  • Observability: additive omitempty meta.retry_count / meta.degrade_reason (schema stays 1, ADR-0027 precedent) + two --verbose footer lines. Live-call-only.

CLI plain-text providers, the degrade contract, and the cache key are untouched.

Tests

  • findings_test.go: fence salvage across fence spellings + typed error-kind classification.
  • structured_test.go: repair recovery (prose reset / truncated complete), degrade reason, cache-replay of fenced content, usage summing.
  • render_test.go: retry_count / degrade_reason JSON omit-when-zero / present-when-set + verbose footer; v1 golden confirms byte-stability.
  • make check green (fmt / vet / lint / test / release-check / i18n-check / spdx-check / gosec); make eval green.

Docs (.ssot + wiki) synced separately: new ADR-0031, ADR-0014 §Update, json-schema-v1 §Update.

Replace the blind byte-identical structured-output retry with a recovery
ladder: fence salvage -> classify -> repair-oriented retry -> degrade.

- Phase 0: ParseFindings unwraps a lone ```json fence pair before
  json.Unmarshal, so fenced-valid JSON parses with zero retries. Non-fenced
  input is byte-identical, so the cache key is unchanged and cached FormatJSON
  fenced bodies render as findings on replay.
- Phase 1+2: ParseFindings returns a typed *ParseError{Kind}
  (empty/prose/malformed-json/schema; message text unchanged so existing
  substring assertions hold). tryStructuredReview classifies the failure and
  sends a failure-mode-specific repair prompt instead of the identical request
  -- a hard "JSON only" reset for prose/schema-ignored, or a "complete the
  JSON" nudge (partial output embedded, max_tokens raised) for a truncated
  attempt. The retry stays a fresh single-shot request; the markdown-fallback
  terminal state and summed usage are unchanged.
- Observability: additive omitempty meta.retry_count / meta.degrade_reason
  (schema stays 1) + two --verbose footer lines; live-call-only.

CLI plain-text providers, the degrade contract, and the cache key are
untouched.
@muhammetsafak
muhammetsafak merged commit df916cf into main Jul 4, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace blind structured-output retry with a repair-oriented prompt

1 participant