Son of Anton Docs
Reference

Review Output Schema

The JSON structure every semantic review must return, and how it maps to the posted review body.

Semantic review is requested as one JSON object. The runner applies a hand-written usability check to parsed output; it does not execute a complete JSON Schema validator. Unusable output is a semantic-analysis failure. Usable hypothesis coverage can recover it; otherwise the runner throws unless GILF_PUBLISH_ON_CODEX_FAILURE=1 explicitly permits degraded fallback.

Source of truth: REVIEW_SCHEMA in son-of-anton-review/src/codex-review-runner.js:170-281 (R/ below), branch feat/cloudflare-native. The separate son-of-anton-operator-parity branch feat/greptile-operator-parity owns the parity Worker API. Neither branch's endpoints imply the other has been deployed.

Top-level fields

The requested schema requires all fourteen fields. Empty arrays are valid; sequenceDiagram can be empty. The runtime additionally requires nonblank overview, summary and mergeVerdict. Schema requirements and runtime checks differ as described below.

FieldTypeNotes
overviewstringNon-empty. One paragraph on what the PR does.
summarystringNon-empty.
verdictclear | needs-attentionOverall review verdict.
confidenceScoreinteger 1-5Analyst confidence in the conclusions. Not merge readiness.
bulletHighlightsstring[]Rendered as the summary bullet list.
reasoningstringWhy the confidence score is what it is.
mergeVerdictstringNon-empty. The only place merge readiness is stated in prose.
importantFiles{ path, overview }[]Rendered as a table.
findingsfinding[]See below.
priorFindingDispositionsdisposition[]See below. [] when none apply.
missingValidationsstring[]What was not run or could not be inspected.
crossRepoImpactstring[]Downstream repos or consumers affected.
sequenceDiagramstringASCII flow. Empty string allowed.
competitorComparisonobjectSee below.

findings[]

{
  "severity": "blocker | high | medium | low",
  "category": "string",
  "path": "string",
  "title": "string",
  "body": "string",
  "contextIds": ["string"]
}

The requested schema requires all six keys. There is no line, column, end-line, per-finding confidence, suggestion or patch field. additionalProperties: false forbids adding those to a schema-conforming model finding. contextIds must refer only to supplied memory context ids; use [] when none apply (R/src/codex-review-runner.js:210-224,1852). Downstream provenance checks remove unsupported citations; an arbitrary file path is not a memory id.

Blocker and high severities drive the merge status. See Finding Evolution for how findings are fingerprinted across heads.

priorFindingDispositions[]

Used when the same PR head was reviewed before. A missing finding on an unchanged head is not a fix, so the model must either re-report the concern or close it with evidence.

{
  "priorReviewKey": "string",
  "fingerprint": "string",
  "disposition": "resolved | false_positive | no_longer_applicable",
  "explanation": "string",
  "evidence": [{ "headSha": "string", "path": "string", "quote": "string" }]
}

priorReviewKey identifies the source review, fingerprint identifies its finding, and evidence quotes current code at the current headSha. The runner verifies disposition sources and the service reconciles same-head history. Unsupported closure claims are not accepted as resolutions; unresolved concerns add missing-validation limitations and cap confidence at 3 (R/src/codex-review-runner.js:2723-2768; R/src/finding-evolution.js:320-375; R/src/review-service.js:397-413).

competitorComparison

Present on every review. Ten keys, all required:

FieldType
availableboolean
winnergilf | greptile | split | unclear
summarystring
confidenceinteger 1-5
agreementsstring[]
greptileMissesstring[]
gilfMissesstring[]
strongerGreptileFindingsstring[]
strongerGilfFindingsstring[]
actionItemsstring[]

When nothing was recorded, normalizeReview substitutes this default (src/review-format.js:83-94):

{
  "available": false,
  "winner": "unclear",
  "summary": "No competitor comparison was recorded for this review run.",
  "confidence": 1,
  "agreements": [], "greptileMisses": [], "gilfMisses": [],
  "strongerGreptileFindings": [], "strongerGilfFindings": [], "actionItems": []
}

The review body only renders a "Competitor Benchmark" section when available is true.

Validation

isUsableCodexReviewOutput (R/src/codex-review-runner.js:1912-1939) checks:

  1. Payload is a non-null object, not an array.
  2. overview, summary, verdict, reasoning, mergeVerdict, sequenceDiagram are strings; overview, summary, mergeVerdict must be nonblank.
  3. verdict is clear or needs-attention; confidenceScore is an integer 1 to 5.
  4. bulletHighlights, importantFiles, findings, missingValidations, crossRepoImpact are arrays.
  5. competitorComparison is an object with the required boolean/string/enum/confidence values and six array fields.

It does not enforce nested finding/item shapes, additionalProperties, array element types or the presence of priorFindingDispositions. Emit the complete requested schema anyway; a payload passing this shallow gate is not necessarily a safe or useful review.

File and JSON-event readers return null for unreadable/unusable output (R/src/codex-review-runner.js:1941-1981). HTTP providers throw if no usable review can be extracted (:3037-3038). A failed Codex CLI can return usable partial output, but the runner records missing validation and degraded semantic coverage (:3169-3175). Failure recovery is conditional, not an automatic deterministic success (:2692-2707).

Confidence is not merge readiness

The prompt states it verbatim (src/codex-review-runner.js:1844):

Treat confidenceScore strictly as analyst confidence in the review conclusions, not as merge readiness. Put merge readiness only in mergeVerdict and in the severity of findings.

Merge status is computed downstream from finding severity, mergeVerdict, validation state and analyzer health (normalizeReview, src/review-format.js:20-101). The confidence score only gates the check run when statusChecks.requiredConfidence is set in the review policy. See Dashboard Settings.

Mapping to the posted review

Rendering lives in buildReviewMarkdown and buildCheckSummary (R/src/review-format.js:127-247). The check name defaults to Son of Anton / review, subject to GILF_PRODUCT_NAME. publishCheckRun patches when a check id is supplied and creates otherwise. publishReview posts a new review body; it does not update an existing review in place (R/src/github-publisher.js:22-66).

Schema fieldReview body sectionCheck run field
overview, bulletHighlightsSummary
derived mergeStatus + mergeVerdictMerge Status: CLEAR / CAUTION / BLOCKoutput.summary prefix [CLEAR] etc, conclusion
confidenceScore, reasoningAnalyst Confidence: n/5output.summary suffix "Analyst confidence n/5."
importantFilesImportant Files Changed (table)
findings (inside changed paths)Findingsoutput.text, one line per finding
findings (outside changed paths)Outside-Diff Findings
missingValidationsValidation Status, prefixed MISSING:affects conclusion
crossRepoImpactCross-Repo Impact
competitorComparisonCompetitor Benchmark (only when available)
sequenceDiagramASCII Flow (fenced text block)
findings titles + pathsPrompt to Fix

reviewCheckConclusion returns failure for missing/below-threshold confidence when the threshold is positive. Non-clear merge status returns neutral. With a positive threshold, missing/incomplete/unpinned semantic coverage also returns neutral, not success. Otherwise it returns success (R/src/review-format.js:138-149). Section visibility follows pinned policy, but safety and validation remain visible. See Publish modes.

Evidence artifact refs

With GILF_EVIDENCE_ARTIFACTS=1, the runner attempts to persist validation/worker output and attach matching references (R/src/codex-review-runner.js:3517-3569; R/src/evidence-artifacts.js:492-517). This is a post-model extension, not a key requested from the model:

"evidenceArtifacts": [{ "id": "…", "key": "…", "url": "…", "kind": "…" }]

The body renders Evidence: with artifact kind and URL, or storage key when no URL exists. GILF_EVIDENCE_REQUIRE_ARTIFACTS=1 marks execution claims without artifacts using evidenceStatus: "unverified" and downgradedReason; it does not lower severity or remove them (R/src/evidence-artifacts.js:520-558; R/src/review-format.js:164-181).

The require flag has no effect if artifact processing is disabled. If artifact processing itself throws, the runner records evidence_artifacts_failed and returns the original findings. Neither evidence flag is forwarded into native Cloudflare containers, so Worker env changes alone cannot activate this behavior (R/cloudflare-native/src/container-env.js:15-86). See Validation and Evidence.

Storage and exposure

  • Accepted review data is stored as review, with findings, verdict, summary and competitor comparison also on the run (R/src/review-service.js:419-426). SQLite's review_runs.record_json stores the run record.
  • The Node dashboard's GET /operator/api/pr?repo=owner/name&number=1 returns summarized runs, flattened findings and events, not full raw run objects (R/src/operator-dashboard.js:405-427). Its normalized display shape may include a nullable line; that does not add line information to the model schema.
  • The expanded parity Worker API is separate from this Node handler and native public ingress. See Operator API.

Full example

The following is an illustrative payload, not a recorded review or a claim about this repository:

{
  "overview": "Adds retry with jitter to the webhook consumer and widens the dead-letter path.",
  "summary": "Retry logic is correct. Dead-letter widening drops the original error.",
  "verdict": "needs-attention",
  "confidenceScore": 4,
  "bulletHighlights": [
    "Retry backoff capped at 5 attempts",
    "Dead-letter payload no longer carries the original error"
  ],
  "reasoning": "Diff and surrounding consumer code were fully available. Tests were not run.",
  "mergeVerdict": "Safe to merge with 1 high-risk follow-up.",
  "importantFiles": [
    { "path": "src/consumer.js", "overview": "Retry loop and dead-letter handoff." }
  ],
  "findings": [
    {
      "severity": "high",
      "category": "correctness",
      "path": "src/consumer.js",
      "title": "Dead-letter message discards original error",
      "body": "toDeadLetter(msg) builds a new envelope without msg.error, so operators lose the failure cause.",
      "contextIds": []
    }
  ],
  "priorFindingDispositions": [],
  "missingValidations": ["Test suite not executed for this head."],
  "crossRepoImpact": [],
  "sequenceDiagram": "webhook -> consumer -> retry(5) -> dead-letter",
  "competitorComparison": {
    "available": false,
    "winner": "unclear",
    "summary": "No competitor comparison was recorded for this review run.",
    "confidence": 1,
    "agreements": [],
    "greptileMisses": [],
    "gilfMisses": [],
    "strongerGreptileFindings": [],
    "strongerGilfFindings": [],
    "actionItems": []
  }
}

No line location or artifact is invented in this example. A finding path alone is insufficient to post a GitHub inline review comment.

On this page