# How the Codex Plugin Handles Structured Output Parsing for Review Results

> Discover how the Codex plugin uses strict JSON schema validation to parse raw review text into typed objects with guaranteed fields for review results.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-07-28

---

**The Codex plugin enforces strict JSON schema validation to convert raw adversarial review text into typed objects with guaranteed fields like `verdict`, `summary`, and `findings`.**

The `openai/codex-plugin-cc` repository implements a robust pipeline for **structured output parsing for review results**, ensuring that adversarial code reviews return machine-readable data instead of free-form text. By combining JSON Schema definitions with runtime validation, the plugin enables downstream tools to consume review findings without fragile string parsing.

## The Four-Step Structured Output Pipeline

The plugin processes adversarial reviews through a deterministic four-stage pipeline defined in `plugins/codex/scripts/codex-companion.mjs` and supporting libraries.

### 1. Loading the JSON Schema Definition

At initialization, the plugin loads the strict type definition from [`plugins/codex/schemas/review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/schemas/review-output.schema.json). This file mandates specific fields including `verdict` (enum: `["approve", "needs-attention"]`), `summary`, `findings` (array of severity-annotated issues), and `next_steps`.

In `codex-companion.mjs` lines 15–68, the schema is imported via `readOutputSchema`:

```javascript
const REVIEW_SCHEMA = path.join(ROOT_DIR, "schemas", "review-output.schema.json");
const schema = readOutputSchema(REVIEW_SCHEMA);

```

This schema object serves as the contract between the plugin and the Codex app-server.

### 2. Enforcing Schema Compliance via outputSchema

When executing an adversarial review, the plugin passes the loaded schema to `runAppServerTurn` using the `outputSchema` parameter (lines 415–416). This instructs the app-server to validate the model's final output against the schema before returning it:

```javascript
const result = await runAppServerTurn(context.repoRoot, {
  prompt,
  model,
  sandbox: "read-only",
  outputSchema: readOutputSchema(REVIEW_SCHEMA)  // Schema enforcement point
});

```

If the model can comply, the server returns a JSON-encoded string matching the schema exactly; otherwise, the request fails at the server level.

### 3. Safe JSON Parsing with parseStructuredOutput

After the turn completes, the raw text in `result.finalMessage` is processed by `parseStructuredOutput` exported from `plugins/codex/scripts/lib/codex.mjs` (lines 88–113). This utility attempts `JSON.parse` and returns a standardized result object:

```javascript
const parsed = parseStructuredOutput(result.finalMessage, {
  status: result.status,
  failureMessage: result.error?.message ?? result.stderr
});

// Returns: { parsed: object|null, rawOutput: string, parseError: string|null }

```

On success, `parsed.parsed` contains the typed review object; on failure, `parseError` contains the exception message and `rawOutput` preserves the original string for debugging.

### 4. Propagating Typed Results to Downstream Consumers

The companion script stores the validated object in the job payload (lines 418–424 and 426–440), making it available to UI renderers and test suites:

```javascript
return {
  payload: {
    result: parsed.parsed,        // Structured object matching schema
    rawOutput: parsed.rawOutput,  // Original JSON string
    parseError: parsed.parseError // Null when parsing succeeds
  }
};

```

Downstream code in `plugins/codex/scripts/lib/render.mjs` (`renderReviewResult`) consumes this payload directly, accessing `payload.result.verdict` and `payload.result.findings` without secondary parsing.

## Review Output Schema Structure

The schema defined in [`plugins/codex/schemas/review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/schemas/review-output.schema.json) requires four top-level properties:

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["verdict", "summary", "findings", "next_steps"],
  "properties": {
    "verdict": { 
      "type": "string", 
      "enum": ["approve", "needs-attention"] 
    },
    "summary": { "type": "string", "minLength": 1 },
    "findings": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "severity", "title", "body", "file",
          "line_start", "line_end", "confidence", "recommendation"
        ],
        "properties": {
          "severity": { 
            "type": "string", 
            "enum": ["critical", "high", "medium", "low"] 
          },
          "title": { "type": "string", "minLength": 1 },
          "body": { "type": "string", "minLength": 1 },
          "file": { "type": "string", "minLength": 1 },
          "line_start": { "type": "integer", "minimum": 1 },
          "line_end": { "type": "integer", "minimum": 1 },
          "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
          "recommendation": { "type": "string" }
        }
      }
    },
    "next_steps": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 }
    }
  }
}

```

Each finding includes precise location metadata (`line_start`, `line_end`) and a confidence score, enabling automated triage workflows.

## End-to-End Implementation Flow

The complete integration from schema loading to result consumption follows this pattern:

```javascript
import { readOutputSchema, parseStructuredOutput } from './lib/codex.mjs';
import { runAppServerTurn } from './codex-companion.mjs';

// 1. Load schema
const REVIEW_SCHEMA = path.join(ROOT_DIR, "schemas", "review-output.schema.json");

// 2. Execute review with structured output enforcement
const result = await runAppServerTurn(repoRoot, {
  prompt: "Check for race conditions in the caching layer",
  model: "gpt-5.4-mini",
  sandbox: "read-only",
  outputSchema: readOutputSchema(REVIEW_SCHEMA)
});

// 3. Parse structured output
const parsed = parseStructuredOutput(result.finalMessage, {
  status: result.status,
  failureMessage: result.error?.message
});

// 4. Access typed data
if (parsed.parsed) {
  console.log(`Verdict: ${parsed.parsed.verdict}`);
  console.log(`Findings: ${parsed.parsed.findings.length} issues detected`);
}

```

## Testing and Validation

The test harness in `tests/fake-codex-fixture.mjs` validates that the plugin correctly transmits the schema. Lines 57–60 check for the presence of the `verdict` property in the `outputSchema` parameter:

```javascript
const payload = message.params.outputSchema?.properties?.verdict
  ? structuredReviewPayload(prompt)   // Returns mock JSON matching schema
  : taskPayload(prompt, ...);

```

This ensures that tests accurately simulate the server behavior: when the schema is present, the fixture returns structured JSON; otherwise, it returns plain text. The tight coupling between the runtime schema transmission and test validation prevents regressions in **structured output parsing for review results**.

## Programmatic Usage Examples

### Invoking Adversarial Reviews from Claude Code

Trigger a structured review via the CLI command:

```bash
/codex:adversarial-review --base main \
  "Analyze thread-safety in the connection pool implementation"

```

This command invokes `executeReviewRun` with `reviewName: "Adversarial Review"`, which automatically loads the JSON schema and parses the structured result according to the four-step pipeline.

### Accessing Parsed Results in JavaScript

Consume review results programmatically without manual JSON parsing:

```javascript
const reviewJob = await codex.runAdversarialReview({
  cwd: repoRoot,
  base: "main",
  focusText: "thread-safety",
  model: "gpt-5.4-mini"
});

if (reviewJob.payload.result) {
  console.log("Verdict:", reviewJob.payload.result.verdict);
  console.log("Summary:", reviewJob.payload.result.summary);
  
  // Iterate over typed findings
  reviewJob.payload.result.findings.forEach(finding => {
    console.log(`[${finding.severity}] ${finding.title} (${finding.file}:${finding.line_start})`);
  });
}

```

The `payload.result` object is fully typed and guaranteed to contain the fields defined in [`review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/review-output.schema.json).

### Customizing Output Schemas

For specialized review types, create a custom schema file and modify `codex-companion.mjs`:

```javascript
const SECURITY_SCHEMA = path.join(ROOT_DIR, "schemas", "security-review.schema.json");

const result = await runAppServerTurn(context.repoRoot, {
  prompt,
  model,
  sandbox: "read-only",
  outputSchema: readOutputSchema(SECURITY_SCHEMA)  // Custom schema injection
});

```

The existing `parseStructuredOutput` utility handles custom schemas transparently, attempting to parse the returned JSON and surfacing errors via the `parseError` field if validation fails.

## Summary

- **Schema-driven contracts**: The plugin loads [`review-output.schema.json`](https://github.com/openai/codex-plugin-cc/blob/main/review-output.schema.json) to define mandatory fields (`verdict`, `findings`, `next_steps`) for all adversarial reviews.
- **Server-side enforcement**: Passing `outputSchema` to `runAppServerTurn` compels the Codex app-server to return JSON matching the schema or fail explicitly.
- **Safe parsing layer**: `parseStructuredOutput` in `lib/codex.mjs` wraps `JSON.parse` to return either a typed object or a detailed error without throwing.
- **Typed consumption**: Downstream code receives `payload.result` as a validated object, eliminating the need for ad-hoc regex parsing or string manipulation.
- **Test parity**: The fixture in `tests/fake-codex-fixture.mjs` mirrors runtime behavior by detecting the schema's `verdict` property to return appropriate mock data.

## Frequently Asked Questions

### What happens if the model returns invalid JSON?

The `parseStructuredOutput` function catches parsing exceptions and returns an object where `parsed` is `null`, `parseError` contains the error message, and `rawOutput` preserves the original string. This allows the plugin to gracefully handle model hallucinations or schema violations without crashing the review pipeline.

### Can I modify the review output schema?

Yes. You can create a new JSON schema file and pass it to `runAppServerTurn` via the `outputSchema` parameter. However, downstream components like `renderReviewResult` expect the standard fields (`verdict`, `findings`, etc.), so custom schemas require corresponding updates to the rendering logic.

### How does the Codex app-server enforce the schema?

When the `outputSchema` option is provided in `runAppServerTurn`, the app-server validates the model's final turn output against the schema before returning it to the plugin. If the model cannot produce valid JSON conforming to the schema, the server returns an error status instead of malformed text.

### Where is the parsed review result stored?

After parsing, the structured object is stored in `reviewJob.payload.result` (lines 426–440 in `codex-companion.mjs`). This location is consistent across adversarial reviews and is accessed by both the CLI renderer in `lib/render.mjs` and external consumers of the plugin API.