Skip to content

fix(review): make frozen candidate artifacts consumable by OpenCode reviewers #1958

Description

@Thrasno

Pre-flight Checklist

  • I have searched existing issues and this is not a duplicate
  • I understand that PRs will be rejected if the linked issue does not have status:approved

📝 Bug Description

A negotiated high-risk review can freeze the candidate successfully but still become impossible to complete in OpenCode because the exact candidate diff is returned as one large base64 value inside the review-start JSON envelope. The reviewer actors cannot consume that exact artifact when their read surface truncates long lines, and they may also lack a file-writing tool for the required result artifact.

This creates a dead-end after review START: all selected lenses are bound to the frozen subject, but none can truthfully report inspection.status: completed; no results can be captured; and review finalize remains blocked waiting for every original reviewer result. Retrying would violate the one-run-per-lens contract, while fabricating completion would violate review authority.

The bug is in the artifact handoff between the compact facade and OpenCode reviewer actors, not in the reviewed repository.

🔄 Steps to Reproduce

  1. In a Git repository with a sufficiently large uncommitted candidate, run the negotiated review-start flow for a high-risk candidate and obtain four selected lenses.
  2. Observe that candidate_diff is embedded as a single base64-encoded JSON string (approximately 160 KB in the observed run), while the changed-path manifest is embedded in the same response.
  3. Launch each bound OpenCode reviewer with the exact lineage, target, lens, order, and subject hash, instructing it to inspect the frozen artifact.
  4. The reviewer read surface truncates the long base64 line before it can decode the complete candidate diff. In runtimes without a writable artifact tool, the reviewer also cannot create the required BOM-less result JSON file.
  5. Run gentle-ai review finalize --lineage <lineage>. It refuses to finalize because all original reviewer results are missing.

✅ Expected Behavior

The negotiated START response should provide each reviewer with a lossless, directly consumable frozen diff and changed-path manifest. Prefer provider-owned opaque handles or materialized artifact files with stable paths and hashes, rather than a large single-line base64 payload.

Each selected reviewer should be able to read the exact immutable candidate, inspect every changed path in manifest order, return or persist its bound result artifact even without generic filesystem writes, and let the facade capture an explicit completed or typed-unavailable result so finalize reaches a terminal state without fabricating PASS.

❌ Actual Behavior

The facade freezes the candidate and binds the lenses, but the only delivered diff representation is not consumable by the reviewer runtime. Reviewers return unavailable because the payload is truncated and/or the required result file cannot be written. capture-result cannot accept those incomplete inspections, and finalize remains permanently blocked waiting for the four original results.

A related usability issue is that calling review finalize without --lineage when multiple lineages exist emits operation_outcome_unknown and generates a defect report, even though the caller-actionable remedy is simply to specify the lineage.

Gentle AI Version

2.2.0 (ee83e83)

Operating System

Linux (Other), amd64

AI Agent / Client

OpenCode

📋 Affected Area

CLI (commands, flags) / review integration contract

💡 Logs / Error Output

Unable to complete: the immutable candidate diff is base64 on one line and was truncated by the available read tool.

Cannot review: the candidate diff/manifest cannot be consumed losslessly and no result artifact can be written.

review finalize requires all original reviewer results; capture each missing one with gentle-ai review capture-result.

Additional Context

No repository name, local path, candidate content, user identity, lineage identifier, target hash, or project-specific information is included intentionally.

Potential fixes:

  1. Materialize the frozen diff and manifest as provider-owned files before reviewer launch and expose stable opaque handles/paths in START.
  2. Offer a reviewer result-capture channel that accepts the returned JSON object directly, without requiring model-owned filesystem writes.
  3. Persist typed unavailable lens outcomes so a review can terminate safely instead of remaining indefinitely non-finalizable.
  4. Classify the missing---lineage finalize case as invalid_request or selection_required, not operation_outcome_unknown.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions