// tests/validators/review-validator-annotation-fields.test.mjs // Pin forward-compat for v4.2 annotation frontmatter fields on review.md. // Adding revision/source_annotations/annotation_digest/revision_reason must NOT // trigger REVIEW_UNKNOWN_FIELD or similar — validator is purely additive-tolerant // per source_findings precedent. No code change required; this test pins the policy. import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { validateReviewContent } from '../../lib/validators/review-validator.mjs'; const BASE_REVIEW = `--- type: trekreview review_version: "1.0" created: 2026-05-09 task: "Annotated revision forward-compat" slug: ann-fwd-compat project_dir: .claude/projects/2026-05-09-ann-fwd-compat/ brief_path: .claude/projects/2026-05-09-ann-fwd-compat/brief.md scope_sha_start: abc123 scope_sha_end: def456 reviewed_files_count: 1 findings: [] --- # Review ## Executive Summary Verdict: ALLOW. ## Coverage | File | Treatment | Reason | |------|-----------|--------| | lib/foo.mjs | deep-review | risk | ## Remediation Summary None. `; test('review-validator forward-compat — baseline (no annotation fields) still valid', () => { const r = validateReviewContent(BASE_REVIEW, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — accepts revision: 0', () => { const t = BASE_REVIEW.replace('findings: []', 'findings: []\nrevision: 0'); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — accepts revision: 5', () => { const t = BASE_REVIEW.replace('findings: []', 'findings: []\nrevision: 5'); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — accepts source_annotations alongside source-style findings', () => { const inject = `\nrevision: 1\nsource_annotations:\n - id: ANN-0001\n target_artifact: review.md\n target_anchor: executive-summary\n intent: question\n comment: "wording is ambiguous"\n timestamp: "2026-05-09T10:00:00Z"`; const t = BASE_REVIEW.replace('findings: []', 'findings: []' + inject); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — accepts annotation_digest string', () => { const t = BASE_REVIEW.replace('findings: []', 'findings: []\nrevision: 1\nannotation_digest: 0123456789abcdef'); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — accepts revision_reason for non-additive revision', () => { const t = BASE_REVIEW.replace('findings: []', 'findings: []\nrevision: 2\nrevision_reason: "removed coverage section"'); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — all 4 annotation fields together still valid', () => { const inject = `\nrevision: 3\nrevision_reason: "applied 2 annotations"\nannotation_digest: 0123456789abcdef\nsource_annotations:\n - id: ANN-0001\n target_artifact: review.md\n target_anchor: coverage\n intent: change`; const t = BASE_REVIEW.replace('findings: []', 'findings: []' + inject); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('review-validator forward-compat — unrecognized future field tolerated', () => { const t = BASE_REVIEW.replace('findings: []', 'findings: []\nfuture_v4_3_key: "any"'); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); });