Skip to content

feat(review): remove --post, drop JSON contract, let Claude handle posting#70

Merged
JohnnyVicious merged 1 commit intomainfrom
feat/remove-post-let-claude-handle
Apr 14, 2026
Merged

feat(review): remove --post, drop JSON contract, let Claude handle posting#70
JohnnyVicious merged 1 commit intomainfrom
feat/remove-post-let-claude-handle

Conversation

@JohnnyVicious
Copy link
Copy Markdown
Owner

Why

Third attempt at `--post` exposed that parser-side heuristics are the wrong abstraction:

  • v1.0.9: hit the undici body-timeout bug
  • v1.0.10: added a stderr handoff trailer to shift posting to Claude Code (dropped `gh api --paginate`)
  • v1.0.11: taught the parser to tolerate unescaped inner quotes in review JSON
  • Then a real adversarial-review run on PR #412 produced valid JSON of a different shape (`{findings: [{summary, findings: [...]}]}` with no top-level `verdict`), the schema-aware parser rejected it, the companion fell through to printing raw JSON, and the posted PR review was raw JSON embedded in a code block again.

Chasing model-invented schemas is a losing game. The pivot we converged on in the plan thread: rip the whole flag out, drop the JSON output contract entirely, and let Claude Code handle posting via its own Bash tool when the user asks.

What changes

Removed

  • `--post` and `--confidence-threshold` flags from both review handlers
  • `plugins/opencode/scripts/lib/pr-comments.mjs` (payload builder, trailer emitter, gh plumbing)
  • `plugins/opencode/scripts/lib/review-parser.mjs` (schema-aware JSON parser)
  • `renderReview` from `render.mjs` — no callers left
  • 48 tests across 3 files covering the above
  • `parseConfidenceThreshold` and `emitPostTrailer` helpers in the companion
  • The `<structured_output_contract>` section of the adversarial prompt
  • The "respond in JSON matching the review-output schema" line in the standard review prompt
  • The "Post-review publishing" section in both slash-command docs

Added

  • A simple per-finding prose shape in both review prompts: `### — <title>`, `File:`, `Confidence:`, analysis paragraphs, `Recommendation:`. Severities are `LOW | MEDIUM | HIGH | CRITICAL`.
  • An "Optional: post the review to GitHub" section in both slash-command docs that fires only when the user explicitly asked in natural language ("and post it", "comment it back", "publish the review"). A bare `--pr 412` is NOT a request to post.
  • The post flow: Claude composes its own markdown summary in its voice, writes a `gh api … /reviews` payload to a temp file via the `Write` tool, POSTs via a single Bash call, extracts `html_url`, and appends a one-line status. Never `REQUEST_CHANGES` — always `COMMENT`.

Net effect

  • Companion is simpler — raw review text goes in, raw review text comes out (with the existing model header).
  • Chat output is readable prose the model produced directly. No more "ugly JSON dumped in chat" failure mode because there is no JSON contract to fail.
  • Posting is Claude's job, where it belongs — Claude has full editorial discretion over the summary body and full access to `gh`.

Test plan

  • `npm test` → 220/220 pass (down from 268 because 48 cases covered the removed post-plumbing)
  • Manual: re-run `/opencode:adversarial-review --pr 412 --background` and confirm the chat output renders as readable markdown prose (no more raw JSON dumps)
  • Manual: add "and post it back" to the request on a small test PR and confirm Claude composes a clean summary and publishes via `gh api`

Out of scope (explicitly)

  • Bringing deterministic inline comments back. If the user wants that later, we'd add a well-scoped helper Claude could call — not re-introduce parser heuristics.

Third attempt at --post exposed that parser-side heuristics are the
wrong abstraction. v1.0.9 hit the undici body-timeout bug. v1.0.10
added a stderr handoff trailer to shift posting to Claude Code.
v1.0.11 tolerated unescaped inner quotes. Then a real run produced
*valid* JSON of a *different shape* (`{findings: [{summary, findings: [...]}]}`
with no top-level `verdict`), rejected by the schema-aware parser,
falling through to raw-JSON-in-chat and raw-JSON-in-the-PR-comment
again.

Chasing model-invented schemas is a losing game. Fix: remove the
whole flag, drop the JSON output contract entirely, and let Claude
Code handle posting via its own Bash tool when the user asks.

Changes:
  * opencode-companion.mjs: remove --post/--confidence-threshold
    flags from both review handlers, strip the tryParseReview +
    renderReview path so review output is now plain prose passed
    through unchanged (with the existing model header), delete
    parseConfidenceThreshold and emitPostTrailer.
  * Delete plugins/opencode/scripts/lib/pr-comments.mjs and
    plugins/opencode/scripts/lib/review-parser.mjs entirely.
  * Delete tests/pr-comments.test.mjs and tests/review-parser.test.mjs.
  * render.mjs: drop renderReview (no callers left). render.test.mjs:
    drop its test suite.
  * prompts/adversarial-review.md: replace structured_output_contract
    with an output_format section that asks for plain markdown
    prose with a per-finding heading/file/confidence shape.
  * lib/prompts.mjs: same treatment for the standard review prompt.
  * commands/review.md and adversarial-review.md: remove --post /
    --confidence-threshold from arg-hint and flag list, delete the
    "Post-review publishing" trailer-parsing section, add an
    "Optional: post the review to GitHub" section that fires ONLY
    when the user explicitly asked in natural language. Claude
    composes its own summary body, writes the gh review payload to
    a temp file via the Write tool, POSTs via `gh api ... --input`,
    and reports html_url. Never REQUEST_CHANGES — always COMMENT.

Net effect: the companion is simpler, the chat output is prose the
user can actually read, and posting is Claude's job (where it
belongs, because Claude has full editorial discretion and full
access to gh).

Tests: 220/220 pass (down from 268 because 48 cases covered the
removed post-plumbing). No regressions in remaining suites.
@JohnnyVicious JohnnyVicious merged commit c13a9c7 into main Apr 14, 2026
1 check passed
@JohnnyVicious JohnnyVicious deleted the feat/remove-post-let-claude-handle branch April 14, 2026 19:19
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.

1 participant