# Triage JSON Recovery Design

## Summary

Prevent `/code-review` from becoming incomplete when the optional triage stage twice returns natural-language analysis instead of its JSON contract. An invalid triage decision safely defaults to reviewing the pull request. At the same time, isolate every JSON repair attempt from the repository and original review task so the repair agent formats the prior response instead of repeating the code review.

This addresses the observed failure on packageauth PR 262 with `volc-ark/glm-5.2` at `max` thinking, where both triage and repair returned prose beginning with a repository analysis.

## Goals

- Continue review when triage alone cannot produce valid JSON after one repair attempt.
- Preserve fail-closed behavior for model execution, authentication, process, cancellation, and required-stage failures.
- Make repair calls formatting-only and independent of repository access.
- Keep JSON parsing strict and deterministic.
- Show the triage fallback clearly in the retained progress messages.

## Non-goals

- Treating invalid reviewer, validator, or summary output as successful.
- Extracting JSON-looking substrings from arbitrary prose.
- Adding provider-specific structured-output APIs.
- Changing the number of repair attempts.
- Changing review policy, confidence thresholds, or publication rules.

## Failure Classification

The orchestrator distinguishes two failure classes:

1. **Invalid structured output:** the agent call succeeds, but both the original response and one repair response fail the stage parser.
2. **Execution failure:** the subprocess cannot start, exits unsuccessfully, is cancelled, loses authentication, or otherwise fails before a response can be parsed.

Only the first class receives triage fallback behavior. Execution failures remain incomplete or aborted. Invalid structured output from summary, reviewers, and validators also remains incomplete because those stages produce required review evidence.

## Triage Fallback

Triage remains an optimization for skipping automated or clearly trivial pull requests. Its policy already states that ambiguity means review. Therefore, when the triage output remains invalid after repair, the orchestrator uses:

```json
{
  "review": true,
  "reason": "Triage did not return structured output; continuing with review by default."
}
```

The orchestrator emits a dedicated progress event before continuing to summary. The Pi presenter retains a concise milestone explaining the fallback. A formatting failure is not reported as a clean triage decision and is not hidden from the user.

## Repair Isolation

### Dedicated Prompt

The repair prompt is loaded without the shared review prompt. It describes one responsibility: transform the supplied prior response into exactly one JSON object matching the supplied contract. It explicitly prohibits repository analysis, commentary, Markdown fences, and invented facts.

### Minimal Repair Task

The repair call receives only:

- The exact JSON contract for the current stage.
- The invalid prior response.
- The parser validation error.

It does not receive the original task, snapshot manifest path, PR summary, candidate task, or repository instructions. The invalid response is delimited as untrusted source material.

Every parsed stage supplies its repair contract explicitly:

- Triage decision schema.
- Summary schema.
- Reviewer output schema.
- Validator output schema.

### No Tools

Repair executions explicitly disable all Pi tools. Normal triage, summary, reviewer, and validator executions retain the existing read-only tool allowlist. The subprocess request represents the distinction as an optional tool list: omitted means the existing read-only defaults, while an empty list emits `--no-tools`.

## Parser Behavior

The parser continues to accept a complete plain JSON object or a complete JSON Markdown fence, as it does today. It does not search prose for balanced braces or accept partial objects. This prevents natural-language text, source snippets, or malicious PR content from being mistaken for the stage response.

After a second parser failure, the execution helper throws a typed invalid-output error containing the stage name and safe response preview. The triage caller catches only this typed error. All other callers allow it to produce the existing incomplete result.

## Progress and Reporting

A new `triage-fallback` progress event carries the safe fallback reason. The presenter creates a milestone such as:

```text
Triage fallback

Triage did not return structured output; continuing with review by default.
```

The next visible stage becomes task summarization. Final results and GitHub publication behave normally if every required stage succeeds.

## Testing

- Verify valid triage behavior is unchanged.
- Verify invalid triage plus valid repair uses the repaired decision.
- Verify invalid triage plus invalid repair continues to summary and completes the review.
- Verify the fallback progress event is emitted and rendered as a milestone.
- Verify a triage subprocess execution failure remains incomplete.
- Verify cancellation remains aborted.
- Verify summary, reviewer, and validator double-invalid output remains incomplete.
- Verify repair tasks omit the snapshot path and original task while including the exact contract, invalid response, and validation error.
- Verify repair executions request no tools.
- Verify normal executions retain `read,grep,find,ls`.
- Run formatting, TypeScript checks, the full test suite, package verification, and a PR 262 smoke review when credentials permit.

## Acceptance Criteria

- PR 262 no longer stops solely because triage returns prose twice.
- The user is explicitly told when triage fallback occurs.
- A network, provider, subprocess, or cancellation failure is never converted into a review decision.
- Required-stage malformed output still prevents publication.
- Repair agents cannot read the repository and are not instructed to repeat the original review.
- Strict JSON validation and all existing review safeguards remain intact.
