Skip to content

fix(review): align optional finding IDs with candidate-causal admission #1699

Description

@decode2

Pre-flight Checklist

  • I searched open and closed issues for reviewer finding IDs, optional IDs, canonicalization, candidate-causal admission, proof references, frozen candidates, and changed-path manifests.
  • I understand that no PR should be opened without maintainer approval.

Bug Description

Native reviewer capture accepts facadeFinding.id as optional (json:"id,omitempty"), but candidate-causal admission requires repository-derived finding IDs before canonical lens-result processing assigns deterministic IDs to omitted values.

For a valid candidate-causal finding that omits id, the native pipeline can derive an empty candidate-causal ID from the decoded provider result, then canonicalize the same finding to R1-001, R2-001, R3-001, or R4-001. AdmitArtifact compares the pre-canonical verified ID set with the post-canonical expected ID set and rejects the result as out of scope:

reviewer artifact admission out_of_scope: candidate-causal findings are not proven by repository-derived changed-line evidence

The public decoder therefore permits a shape that native semantic admission cannot successfully process for candidate-causal findings.

This is distinct from intended proof-scope validation. AdmitArtifact correctly rejects proof_refs that cite repository paths outside the frozen changed_path_manifest; callers should keep candidate proof inside the frozen candidate rather than weaken that check.

Steps to Reproduce

  1. Start a negotiated native review with a frozen candidate containing one changed line and select any review lens.
  2. Obtain the provider-owned artifact subject, candidate diff, and changed-path manifest through capture preflight.
  3. Submit a reviewer result with completed provider-owned inspection and one candidate-causal CRITICAL finding whose location and proof reference point to the changed candidate line.
  4. Omit only the finding id; keep evidence_class: "deterministic" and causal_disposition: "introduced".
  5. Capture the result through gentle-ai review capture-result.
  6. Observe that decoding succeeds because facadeFinding.ID is optional, canonicalization assigns a deterministic lens ID, but candidate-causal ID verification no longer matches and admission rejects the result.

Expected Behavior

The native boundary should have one coherent ownership rule for finding IDs:

  • Either require every non-empty finding ID during decode/admission before repository-derived candidate-causal verification, with the canonical selected-lens prefix and uniqueness checks; or
  • Canonicalize deterministic IDs before deriving and comparing repository-owned candidate-causal ID sets, while preserving the exact canonical payload and all existing admission checks.

Missing IDs must not be silently accepted by the schema and then rejected only because canonicalization occurs after causal verification.

Actual Behavior

facadeFinding.ID uses omitempty, canonicalLensResult deterministically supplies a missing ID, and AdmitArtifact compares candidate-causal IDs against the canonicalized result. A schema-valid reviewer payload is therefore unreplayable even though its location and proof are fully inside the frozen candidate.

Environment

  • Gentle AI: 1.49.1-0.20260722122850-8881b60b01c7
  • Operating System: Linux
  • Agent / Client: OpenCode
  • Shell: bash
  • Affected Area: native review capture and artifact admission

Source Evidence

  • internal/cli/review_facade.go: facadeFinding.ID string json:"id,omitempty".
  • internal/reviewtransaction/transaction.go: canonicalLensResult assigns <lens-prefix>-<index> when finding.ID == "".
  • internal/cli/review_artifact.go: repository-derived CandidateCausalFindingIDs are passed into admission.
  • internal/reviewtransaction/artifact_admission.go: admission canonicalizes the lens result, derives candidate-causal IDs from canonical findings, and compares them with the previously verified IDs.

Acceptance Criteria

  • A focused test covers a candidate-causal finding with omitted id and proves the chosen ownership rule deterministically.
  • Native schema/decoder behavior and semantic admission agree about whether id is required.
  • Stable IDs remain bound to the selected lens and unique within the result.
  • Candidate-causal changed-line verification remains fail-closed.
  • Out-of-manifest locations, proof references, and evidence remain rejected.
  • No adapter synthesizes hashes, strips evidence, or rewrites semantic findings to bypass native admission.

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions