# description
Review-tier attestation envelope — the FOURTH tier in IntelligencePro's 5-tier cryptographic chain-of-custody wedge (R16 cross-cutting): pipeline (R14 D1, cycle 314) → experiment (R16 M1, planned) → leaderboard (R15 B1, planned) → REVIEW (R16 R3 ip-merge-gate, THIS — cycle 316) → governance (R14 G1, cycle 315). Closes SYNTHESIS-acquisition Round 16 P2 [[ai-code-review-and-pr-bots]] R3 ship-first action — **the KILLER FEATURE per R16 + R17 + R18 cross-cutting**: ip-merge-gate, the first cryptographic merge gate in market. The R3 ship-first artifact (a GitHub Action that converts an AI code-review verdict into a SIGNED merge gate) is structurally IMPOSSIBLE for CodeRabbit ($60M Series B Sep-2025 @ $550M val / ~$40M ARR / 700% YoY / Aug-2025 Kudelski Security RCE disclosure exposing write access to ~1M repos), Greptile ($180M val / YC W24), qodo (2.0 multi-agent Feb-2026 + PR-Agent OSS handed to community Mar-2026), or GitHub Copilot Code Review (60M+ reviews) to ship themselves — THEY ARE THE AGENTS BEING VERIFIED. IntelligencePro's detached peer attestation = the only architecture that delivers it. Same architectural shape as ip.support.resolution.attestation.v1 (R18 P3 CS1 — planned domain-swap; cs_agent_judging_cs_agent = same as code-review). Designed for: PR-comment embedding (HTML-comment-fenced JSON envelope per R16 P2 ship-first action) so any AI code reviewer can adopt without a new wire format. Composes with R20 EV1 ip.eval.run.attestation.v1 + R20 Q1 ip.retrieval.citation.attestation.v1 + R17 V1 ip.vulnerability.disclosure.v1 (planned) via citedEvidenceShaArray[].

# $comment
Discovery: https://ip.tekton.cc/.well-known/ip-knowledge.json (full descriptor) | Badge: https://ip.tekton.cc/api/badge/v1/attestation/review.attestation.v1/<subjectSha256> (shields.io endpoint) | Free dry-run: POST https://ip.tekton.cc/api/credentials/dry-run with body {kind:"review.attestation.v1", body:<candidate>} (no auth required, validates against this schema) | Verification recipe: https://ip.tekton.cc/docs/verification-recipe.md (3-layer trust model + curl trail) | Revocation: https://ip.tekton.cc/credentials/status/v1 (W3C BitstringStatusList v1.0; check bit at credentialStatus.statusListIndex)

# x-controlMappingRefs.$comment
Cycle 720 (SP-716-P1-4 close): bidirectional back-link to the framework mapping docs. Sister to cycle-685 wiring on alignment / governance-compliance / high-risk-classification / eval.run / synthetic.media. ip.review is the 4th tier in the wedge (R16 R3) and the MANAGE-function workhorse — a Drata/Vanta/OneTrust importer walking the catalog reads these refs to bind a review attestation to the control framework's review/sign-off requirements.

# x-controlMappingRefs.iso42001Aims[].note
Operation — change-management evidence (the ip-merge-gate review IS the sign-off artifact)

# x-controlMappingRefs.iso42001Aims[].note
Performance evaluation — the review verdict + reviewer identity are the audit trail

# x-controlMappingRefs.nistAiRmf[].note
Organize and document risk-management response — the rmfFunctionHint default 'manage' on this schema points here

# x-controlMappingRefs.nistAiRmf[].note
Decision-rights documentation — reviewer signatures bind accountability

# x-controlMappingRefs.euAiAct[].note
Transparency + information to deployers — the review attestation is the human-readable provenance

# x-controlMappingRefs.euAiAct[].note
Quality management system — provider obligation; review attestation = signed QMS artifact

# x-controlMappingRefs.euAiAct[].note
Obligations of deployers — review-attestation chain is the deployer's evidence pack

# x-controlMappingRefs.iso42001RiskRegister[].note
ip-merge-gate enforces signed peer review before merge — closes the unilateral-promotion path

# properties.schemaVersion.description
Semver of this schema. Bumps to 2.0.0 land at /credentials/review/v2 — v1 stays immutable (see /docs/schema-versioning.md for the cycle-726 canonical bump-mechanics + soft-change-detection contract).

# properties.rmfFunctionHint.description
Cycle 487 (cycle-483 follow-up): non-required NIST AI RMF 1.0 function hint. Review attestations map to MANAGE (MANAGE-4.1 'organize and document risk-management response') — the ip-merge-gate decision IS the risk-response artifact. Default 'manage'. Companion to ip.governance.compliance.v1.rmfFunction.

# properties.chain.properties.parentSha256.description
Sha256 of the canonical-JSON bytes of the parent attestation. For a review.attestation typically the parent is the leaderboard-receipt that promoted the reviewed change, OR the experiment-receipt that produced the artifact under review when no leaderboard pass exists. ⚠ Cycle 493 honest-disclosure (adversarial-auditor AA-485-P2-4): the platform does NOT verify parent-existence at issuance. A syntactically-valid 64-hex sha256 satisfies this pattern but the bytes may not resolve in any attestation store. Consumers walking the chain MUST resolve parentSha256 against their own attestation registry (the platform exposes no by-sha lookup as of cycle 493); see /docs/verification-recipe.md for the canonical walk. A dangling parentSha256 is an UNVERIFIED chain link, NOT a verified absence — distinguishing 'platform issued an unresolvable parent pointer' from 'parent was revoked and evicted' requires consumer-side state.

# properties.chain.properties.parentKind.description
Kind identifier of the parent attestation. review.attestation tolerates leaderboard-receipt (the killer ip-merge-gate flow), eval-run (cycle 1123 — a review signed directly on a benchmark eval, e.g. reviewKind data-quality-review / test-coverage-review; eval-run sits below review in the wedge and leaderboard already accepts it as a parent per cycle 499), or experiment-receipt (a review-without-rank workflow).

# properties.chain.properties.parentAttestationId.description
Optional UUID of the parent attestation — convenience for consumers that index by id.

# properties.chain.description
Cycle 484 (RC-480-F2 P0 fix): uniform upward chain link. See ip.experiment.receipt.v1.chain.description for full rationale.

# properties.reviewId.description
Unique RFC 4122 UUID v4 or v7 — this review's canonical identifier. Lets a consumer dedupe across retries + replays and link this attestation back to a ip.attestation.event.v1 wire row via ipAttestationEventId.

# properties.reviewerId.description
Stable handle for the reviewing agent. Examples: 'coderabbit-v3', 'greptile-v3-claude-agent-sdk', 'qodo-2.0', 'github-copilot-code-review', 'sourcegraph-amp', 'wallaby', 'codiumai-pr-agent', 'reviewdog', 'bito', 'aikido-zen'. Lowercase + hyphen + digit + dot so the value doubles as a URL slug + docker-tag fragment. Community owns its own reviewerId namespace; CP doesn't centrally curate.

# properties.reviewerVersionSha256.description
Optional sha256 of the canonical reviewer-release artifact. For PyPI/npm-published: sha256 of the wheel/package. For git-tracked: sha256 of the git tree at the reviewing commit. For closed SaaS: vendor-published version manifest sha. Pins WHICH BUILD of the reviewer ran. Without this, an AI reviewer could silently update its scoring logic between submission and publication.

# properties.reviewKind.description
Classification of the review surface. 'code-review' = standard PR (CodeRabbit/Greptile/qodo). 'security-review' = security-focused (Snyk, Aikido, DeepCode). 'infra-review' = Terraform/IaC. 'schema-review' = OpenAPI/JSON Schema. 'data-quality-review' = dbt tests, Great Expectations. 'lineage-derivation-review' = an INDEPENDENT review of a model's claimed derivation/lineage (R35 cycle 1160 — teacher->student distillation, base_model finetune/merge/quantized, model genealogy): reviewerDid != the model publisher, subjectId = the derived model, subjectSha256 = the model bytes; the verdict carries the reviewer's STATED-CONFIDENCE finding on whether the declared lineage holds (distillation-detection is probabilistic, NOT proof; the review attests an independent CHECK whose own confidence the reviewer states, never a definitive origin proof or an IP/terms-infringement adjudication). Different review kinds carry different evidentiary weight; this routes downstream verifiers.

# properties.subjectId.description
Identifier of what was reviewed. For GitHub PRs: 'github.com/<owner>/<repo>/pull/<n>@<head-sha>'. For GitLab MRs: equivalent. For Gerrit changes: 'change-id@patchset'. For non-PR reviews: any canonical URI. Format is platform-conventional.

# properties.subjectAuthorDid.description
OPTIONAL DID of the AUTHOR of the reviewed subject (the PR author / artifact creator), DISTINCT in the independent case from reviewerDid. Cycle 1154 (supply-chain-auditor sca-1147-P3): the ip-merge-gate's load-bearing independence claim is 'reviewer != author', but pre-cycle-1154 the review VC carried NO author field, so a relying party verifying a STANDALONE review attestation offline could not detect a self-review (reviewerDid == the PR author) from the envelope alone — independence was a property only of the platform's internal judge economy, not of the externally-issued, deployer-signed VC. Populate this with the author's DID to make 'reviewer != author' a CHECKABLE in-band fact. When subjectAuthorDid == reviewerDid the attestation is a SELF-REVIEW and ip-merge-gate consumers SHOULD reject it (or heavily discount); the dry-run validator emits a non-blocking `self_review` warning in that case (mirrors the synthetic-media issuerDid==generatorDid self-attestation disclosure). Omit when the author DID is unknown (back-compat: pre-cycle-1154 records validate unchanged) — but then independence is NOT in-band verifiable and the gate must establish it out-of-band. ⚠ NOT yet in the embedded `signature` pre-image (cycle 1154 disclosure — see the `signature` field): an offline `signature`-only verifier can have this field rewritten/deleted undetected, so for TAMPER-EVIDENT reviewer≠author independence verify the full-body W3C DataIntegrityProof envelope at /credentials/<sha>/v1, not the embedded signature. Until the pre-image is widened (v2 slug), an ip-merge-gate's load-bearing independence enforcement remains the Allowlisted tier (pinned distinct reviewerDids), with subjectAuthorDid as an advisory in-band signal.

# properties.subjectSha256.description
Sha256 of the canonical bytes the review covered. For PR: sha256 of the unified diff at the head-sha when the review fired (NOT the merge-commit — the diff REVIEWED is what's attested). For non-diff reviews (full-codebase or document review): sha256 of the canonical artifact tarball. Required because PR contents are mutable — a force-push after a positive review must invalidate the attestation.

# properties.verdict.description
The review's bottom-line decision. 'approve' = sufficient for merge (ip-merge-gate gate-state = open). 'approve-with-comments' = mergeable but reviewer flagged non-blocking items. 'request-changes' = requires fixes before merge (gate closed). 'comment' = soft-feedback, no gate effect. 'block' = STRONGER than request-changes; reviewer asserts merge would create harm (security vulnerability, regression risk). 'abstain' = reviewer ran but declines to score (out-of-scope, malformed PR). Required: this is the LOAD-BEARING field a ip-merge-gate GitHub Action consults.

# properties.scores.properties.correctness.description
Reviewer's assessed correctness (0..1). 1.0 = no issues identified.

# properties.scores.properties.security.description
Reviewer's assessed security posture (0..1).

# properties.scores.properties.testCoverage.description
Reviewer's assessed test coverage adequacy (0..1).

# properties.scores.properties.composite.description
Weighted composite of sub-scores. Reviewer-defined weights.

# properties.scores.description
Optional per-dimension scores. ip-merge-gate config may consume specific dimensions (e.g., 'block on security < 0.7 even when overall composite is high').

# properties.rationaleSha256.description
Sha256 of the canonical reviewer rationale text. The rationale TEXT itself lives in PR-comment HTML-comment-fenced JSON envelope or in the reviewer's own logging surface — this field binds WHICH rationale was emitted. Lets a downstream auditor cross-check that the published PR comment matches the signed attestation.

# properties.rationaleText.description
Optional human-readable rationale. When present, sha256(canonical(rationaleText)) MUST equal rationaleSha256. Useful for in-line audit display + PR-comment-embedding.

# properties.citedEvidenceShaArray.items.properties.sha256.description
Sha256 of the cited evidence artifact's canonical bytes.

# properties.citedEvidenceShaArray.items.properties.scope.description
Optional scope description (e.g., 'eval-result this PR's new feature relies on', 'vuln disclosure this PR remediates').

# properties.citedEvidenceShaArray.description
Array of cited evidence artifacts the reviewer consulted while reviewing — eval results, sandbox runs, vulnerability disclosures, retrieved-documentation citations. RESOLUTION MODEL (trust-by-sha, like the cycle-493 chain posture): each entry is {kind, sha256} — an INTEGRITY content-address, NOT a resolvable pointer. The platform hosts NO by-sha resolver (GET /api/.../by-sha/<sha> -> 404) and entries carry no locator URL, so a downstream verifier (auditor, opposing-counsel, security-disclosure intake) does NOT dereference the sha. Instead they obtain the cited artifact OUT-OF-BAND — for an attestation-kind entry, locate it via that kind's own surface (e.g. ip.eval.run.attestation.v1 via /api/knowledge/eval/proposals or search; never by-sha); for kind 'external', from its native host — then re-hash the canonical bytes and confirm they equal this sha256. The 'backward walk' is an INTEGRITY check on bytes the verifier already holds, not a platform-side lookup.

# properties.reviewedAt.description
Epoch milliseconds when the reviewer emitted the verdict. Required because PRs evolve over time — a positive verdict at head-sha X may not apply to head-sha Y.

# properties.reviewerDid.description
DID of the reviewer entity. did:web for institutional AI reviewer (did:web:coderabbit.ai, did:web:greptile.com, did:web:qodo.ai); did:key for individual / OSS-self-hosted reviewer (e.g., reviewdog, codiumai-pr-agent). LOAD-BEARING for ip-merge-gate: the gate config pins which reviewerDids count as authoritative.

# properties.mergeGateAsserted.description
True when this attestation is being submitted as evidence for a ip-merge-gate GitHub Action / GitLab status-check / Bitbucket build-status. When true, downstream consumers (the merge-gate action) treat verdict + scores as gating signals. When false, this is an informational attestation only (e.g., posted to a PR comment but not connected to a merge gate).

# properties.humanCoSignerDid.description
Optional DID of a human co-signer (typically a senior engineer or required reviewer) who endorsed the AI reviewer's verdict. ip-merge-gate may require BOTH reviewerDid + humanCoSignerDid for high-risk PRs (production deploys, security-critical paths). Composes with ABA-Rule-3.3-equivalent 'human attorney verified' pattern from R21 LP1.

# properties.supersedes.description
Optional reviewId of a prior review this attestation supersedes. When an AI reviewer re-reviews after a code change, the new attestation supersedes the old (the old attestation's verdict is no longer authoritative for ip-merge-gate). Lets a downstream consumer walk the review chain on a PR with multiple iterations.

# properties.judgesDigest.description
When this attestation results from a multi-reviewer panel (3+ independent reviewers per CP's standard peer-review economy), the canonical digest of the judges manifest.

# properties.ipAttestationEventId.description
When this attestation was emitted as a ip.attestation.event.v1 row (R11 T1) to the platform's pipeline, the eventId of that wire row.

# properties.signature.description
Ed25519 signature (128 hex chars = 64 raw bytes) over the canonical-JSON bytes of (reviewId ⊕ reviewerId ⊕ reviewerVersionSha256? ⊕ reviewKind? ⊕ subjectId ⊕ subjectSha256 ⊕ verdict ⊕ scores? ⊕ rationaleSha256? ⊕ citedEvidenceShaArray? ⊕ reviewedAt ⊕ reviewerDid ⊕ mergeGateAsserted? ⊕ humanCoSignerDid? ⊕ supersedes? ⊕ judgesDigest? ⊕ ipAttestationEventId?). Verify against the publicKey published in the reviewerDid's JWKS. RFC 8037 (Ed25519 in JWS) canonicalization rules apply: JCS (RFC 8785) ordering on the inner object. ⚠ INTEGRITY GAP (aa-824-P0-1, cycle 825 honest disclosure; extended cycle 1154): `chain.*`, `extra`, AND `subjectAuthorDid` are presently OUTSIDE this signature pre-image. A wire-path or post-hoc rewrite of `chain.parentSha256` / `chain.parentKind` / `chain.parentAttestationId` — OR of `subjectAuthorDid` — does NOT invalidate this `signature`. This matters specifically for the reviewer≠author independence check: an offline verifier using ONLY `signature`+`publicKey` can have `subjectAuthorDid` rewritten or deleted (laundering a self-review into an apparently-independent one) WITHOUT a signature mismatch. Offline verifiers using `signature`+`publicKey` (the documented embedded-verification path) MUST treat `chain.*` AND `subjectAuthorDid` as ADVISORY, NOT cryptographically bound to this attestation; for tamper-evident reviewer≠author independence, verify the full-body W3C DataIntegrityProof envelope (below) instead. For full-integrity verification including chain pointers, fetch the W3C DataIntegrityProof envelope from /credentials/<sha>/v1 (which signs the FULL canonical-JSON body including chain) and verify against the proof.cryptosuite=eddsa-jcs-2022 signature. Cycle-484 added the chain block; cycles 778-784 swept it across the schema family; the signature pre-image was not re-derived alongside that sweep — the cycle-825 disclosure documents the gap until a v2 schema slug ($id pin per cycle-462 sp-457-P2-2) widens the pre-image.

# properties.publicKey.description
Ed25519 public key (32 raw bytes = 64 hex) corresponding to the signature above.

# properties.extra.description
Open extension slot — reviewer-specific payload (line-level annotations, severity counts, CodeRabbit-specific learning-from-feedback signals, Snyk vuln IDs). Consumers MUST ignore unknown keys.