Skip to content

feat(reviewer-eval): row-level corpus hashing, split flip stats, FAIL-diff archive, miner outcome labels - #295

Merged
norvalbv merged 2 commits into
mainfrom
bench/reviewer-corpus-infra
Aug 1, 2026
Merged

feat(reviewer-eval): row-level corpus hashing, split flip stats, FAIL-diff archive, miner outcome labels#295
norvalbv merged 2 commits into
mainfrom
bench/reviewer-corpus-infra

Conversation

@norvalbv

@norvalbv norvalbv commented Aug 1, 2026

Copy link
Copy Markdown
Owner

PR A of the reviewer-benchmark corpus-growth effort. Charter: docs/benchmarks/benchmark-methodology.md (living checklist + addendum, moved here from frink in this PR); decision: docs/decisions/benchmarks-grow-from-telemetry.md.

Engine (bench.mts / corpus.mts / stats.mts / progress.mts)

  • Row-set corpusHash — sorted per-row canonical-JSON hashes. Baselines, progress checkpoints, and the stability rerun all pair per-row via rowHash, so appending corpus rows no longer invalidates comparisons or salvage over retained rows (this was structurally blocking the self-feeding-corpus target).
  • compareReviewer pairs on the row-id intersection (changed rows excluded + counted; added/removed reported) and prints pooled + gold-only + decoy-only + clustered-by-case flip tables. New pure stats.mts; checkpoint/salvage IO split to progress.mts (size ratchet).
  • summarize gains cascade-only overturnRate/rescueRate — the July cascade regression (opus overturning true golds, 0.78→0.67) was invisible without them.
  • Sonnet recall floor now gates on effective models, not the BENCH_MODEL env default.
  • validate compiles reasonPattern (hard fail on bad regex) and warns non-fatally on comment-leakage tells.
  • New optional row fields with enum lint: caseId, sourcePr, outcomeEvidence, scopeConfirmed.

Miner (mine-bots.mts + mine-bots-lib.mts)

GraphQL thread resolution, CodeRabbit marker parsing, priority-ordered outcome/outcomeEvidence labels (addressed-marker → bot-withdrawal → resolved+line-touched → human-rebuttal → outdated-only), per-PR line-touched checks, collector scope confirmation (scopeConfirmed/scopedReviewers via commit_ships.pr_number), merge-by-url, alreadyInCorpus flags.

Enabler (evidence/diff-archive.mts)

archiveFailedDiff: content-addressed <telemetry>/diffs/<diff_sha256>.diff.gz on reviewer FAIL. Fail-open, 8MiB cap, fires only from runReviewGate (bench calls runCascade directly and can never archive). Joins telemetry via the existing diff_sha256no event schema change. Stops the ongoing loss of ~12 replayable correctness FAILs/day.

Tests: 441 passing in gate-engine/review/__tests__ incl. new diff-archive, mine-bots, rowHash/pairing/cluster suites. bench.mts validate green for all five suites (0 LLM calls).

Built by a 12-agent workflow (3 scout→code→refute chains + integration barrier); adversarial refuters caught and fixed a real blocker (stability rerun still coupled to the whole-corpus hash).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Failed review diffs can now be securely archived for telemetry and later benchmark analysis.
    • Benchmark evaluations support incremental corpus growth, preserving valid prior results while rerunning changed or failed cases.
    • Added richer review-outcome analysis, including overturn, rescue, stability, and confidence metrics.
    • Expanded tooling to identify and classify bot-review candidates, thread outcomes, scope coverage, and follow-up changes.
  • Documentation

    • Added benchmark methodology and decision records covering telemetry-driven corpus growth, statistical analysis, bias controls, and data handling.
  • Tests

    • Added comprehensive coverage for diff archiving, benchmark evaluation, corpus mining, validation, and reviewer statistics.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a389a40e-2f7b-43dd-aeff-31247d93bc4e

📥 Commits

Reviewing files that changed from the base of the PR and between 565dafb and a5f775f.

📒 Files selected for processing (16)
  • docs/benchmarks/benchmark-methodology.md
  • docs/decisions/INDEX.md
  • docs/decisions/benchmarks-grow-from-telemetry.md
  • eslint/baselines/size-lines.json
  • gate-engine/review/__tests__/diff-archive.test.mts
  • gate-engine/review/__tests__/mine-bots.test.mts
  • gate-engine/review/__tests__/reviewer-eval.test.mts
  • gate-engine/review/eval/reviewers/README.md
  • gate-engine/review/eval/reviewers/bench.mts
  • gate-engine/review/eval/reviewers/corpus.mts
  • gate-engine/review/eval/reviewers/mine-bots-lib.mts
  • gate-engine/review/eval/reviewers/mine-bots.mts
  • gate-engine/review/eval/reviewers/progress.mts
  • gate-engine/review/eval/reviewers/stats.mts
  • gate-engine/review/evidence/diff-archive.mts
  • gate-engine/review/run-review.mts
🚧 Files skipped from review as they are similar to previous changes (14)
  • docs/decisions/benchmarks-grow-from-telemetry.md
  • gate-engine/review/evidence/diff-archive.mts
  • gate-engine/review/tests/diff-archive.test.mts
  • gate-engine/review/tests/mine-bots.test.mts
  • gate-engine/review/run-review.mts
  • gate-engine/review/eval/reviewers/corpus.mts
  • gate-engine/review/eval/reviewers/README.md
  • gate-engine/review/eval/reviewers/mine-bots.mts
  • gate-engine/review/tests/reviewer-eval.test.mts
  • gate-engine/review/eval/reviewers/progress.mts
  • docs/benchmarks/benchmark-methodology.md
  • gate-engine/review/eval/reviewers/mine-bots-lib.mts
  • gate-engine/review/eval/reviewers/stats.mts
  • gate-engine/review/eval/reviewers/bench.mts

📝 Walkthrough

Walkthrough

Changes

Benchmark telemetry and evaluation

Layer / File(s) Summary
Benchmark governance and corpus-growth rules
docs/benchmarks/benchmark-methodology.md, docs/decisions/*
Defines statistical controls, corpus labeling, telemetry capture workflows, human triage limits, and the telemetry-growth decision.
Row hashing, progress, and statistics modules
gate-engine/review/eval/reviewers/corpus.mts, progress.mts, stats.mts
Adds canonical row hashing, row-level checkpoint salvage, baseline loading, comparison statistics, validation helpers, and stability checks.
Row-aware benchmark execution and validation
gate-engine/review/eval/reviewers/bench.mts, README.md, reviewer-eval.test.mts, eslint/baselines/size-lines.json
Updates benchmark execution and comparison to pair rows by ID and hash, report cascade metrics, separate warnings from failures, and validate row-level behavior.
Bot-comment classification and corpus mining
gate-engine/review/eval/reviewers/mine-bots*.mts, gate-engine/review/__tests__/mine-bots.test.mts
Collects review threads and commits, classifies outcomes, checks scope and later changes, enriches candidates, and preserves merged corpus output.
Failed-review diff archival
gate-engine/review/evidence/diff-archive.mts, gate-engine/review/run-review.mts, gate-engine/review/__tests__/diff-archive.test.mts
Archives failed-review diffs with gzip compression, SHA-256 naming, size limits, deduplication, and fail-open handling before telemetry completion.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReviewGate
  participant DiffArchive
  participant BenchmarkMiner
  participant GitHubAPI
  participant BenchmarkCorpus
  ReviewGate->>DiffArchive: archive failed diff bytes
  BenchmarkMiner->>GitHubAPI: fetch threads and commits
  BenchmarkMiner->>BenchmarkCorpus: write enriched candidates
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the pull request's main reviewer-evaluation, corpus, statistics, archival, and miner-labeling changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bench/reviewer-corpus-infra

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
gate-engine/review/eval/reviewers/bench.mts (1)

278-298: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align the escalation condition between overturnRate and rescueRate.

overturnRate.k requires r.escalateLive. rescueRate.k does not. A decoy row that first fails and then passes without a live escalation still counts as a rescue. The two cascade metrics then measure different things.

If both metrics describe opus behavior, apply escalateLive to both numerators, or drop it from both and restrict the denominators to rows that escalated.

🔧 Proposed fix
           rescueRate: {
-            k: count(decoyFirstFail, (r) => r.okFinal),
+            k: count(decoyFirstFail, (r) => r.okFinal && r.escalateLive),
             n: decoyFirstFail.length,
           },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/eval/reviewers/bench.mts` around lines 278 - 298, Align
the cascade eligibility used by overturnRate and rescueRate in the
metrics-building return: update the rescueRate numerator over decoyFirstFail to
use the same r.escalateLive condition as overturnRate, preserving the existing
denominator unless the surrounding metric contract requires otherwise.
🧹 Nitpick comments (8)
gate-engine/review/evidence/diff-archive.mts (1)

49-67: 🔒 Security & Privacy | 🔵 Trivial

Consider a retention policy for the diffs/ store.

By design, this store never prunes: the docstring states it "needs no run-id gate and no per-run pruning." Each unique FAIL diff is retained forever under the telemetry directory, and ARCHIVE_MAX_BYTES caps only the size of a single file, not the total size of the store.

Raw diff text can contain secrets or other sensitive data from the reviewed change. Without any TTL or size-bound sweep, this data accumulates on local disk indefinitely across every FAIL a developer's machine ever produces.

Consider adding a periodic cleanup (age-based or total-size-based) for the diffs/ directory, or documenting the retention expectation for downstream consumers of this archive.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/evidence/diff-archive.mts` around lines 49 - 67, Update
the diff archive flow around archiveFailedDiff to enforce a retention policy for
the diffs store, using periodic cleanup by entry age or total directory size
before or after archiving. Preserve hash-based deduplication and best-effort
failure handling, and ensure cleanup errors never cause a gate to fail.
gate-engine/review/eval/reviewers/mine-bots-lib.mts (2)

61-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider accepting a bot-author set in hasWithdrawal.

hasWithdrawal matches only coderabbitai[bot]. The caller sweeps both coderabbitai[bot] and macroscopeapp[bot]. A withdrawal reply from macroscopeapp[bot] therefore counts as neither a withdrawal nor a human reply, and the comment stays unresolved. hasHumanReply already takes an injectable botAuthors set, so the two helpers are asymmetric.

♻️ Optional: parameterize the withdrawal author set
-export function hasWithdrawal(replies) {
-  return (replies ?? []).some(
-    (r) => r?.author === CODERABBIT_LOGIN && WITHDRAWAL_RE.test(String(r?.body ?? '')),
-  );
-}
+export function hasWithdrawal(replies, botAuthors) {
+  const bots = botAuthors ?? new Set([CODERABBIT_LOGIN]);
+  return (replies ?? []).some(
+    (r) => bots.has(r?.author) && WITHDRAWAL_RE.test(String(r?.body ?? '')),
+  );
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/eval/reviewers/mine-bots-lib.mts` around lines 61 - 69,
Update hasWithdrawal to accept the same injectable botAuthors set used by
hasHumanReply, defaulting to the existing bot author when omitted, and test
membership against that set so withdrawals from both swept bot accounts are
recognized.

105-114: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

isLineTouchedLater checks file-level touches, not line-level touches.

The function only tests c.files.includes(commentPath). Any later commit that touches the same file sets the signal. classifyOutcome then reports fixed with evidence resolved+line-touched for a resolved thread, even if the commented lines never changed. This inflates the fixed label in corpus rows.

The behavior is a deliberate approximation. Rename the signal or state the granularity in the docstring so corpus authors do not read lineTouchedLater as line-level evidence.

♻️ Optional: clarify the granularity in the docstring
-// commits: [{sha, committedDate: ISOString, files: string[]}] — files already fetched by caller.
+// commits: [{sha, committedDate: ISOString, files: string[]}] — files already fetched by caller.
+// Granularity is FILE-level, not line-level: a later commit that touches the commented file sets
+// the signal even when the commented lines are untouched. Treat it as a weak "fixed" hint.
 export function isLineTouchedLater(commits, commentPath, commentCreatedAt) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/eval/reviewers/mine-bots-lib.mts` around lines 105 - 114,
Clarify the documentation for isLineTouchedLater to state that it detects later
file-level touches, not changes to the specific commented lines, and that this
is a deliberate approximation. Keep the existing implementation and
classifyOutcome behavior unchanged.
gate-engine/review/__tests__/mine-bots.test.mts (2)

184-193: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for a resolved thread with no supporting evidence.

The suite does not cover threadResolved: true with lineTouchedLater: false and hasHumanReply: false. That input falls past both resolved branches and returns unresolved with outcomeEvidence: null. The result contradicts the intuitive reading of "resolved", so pin it with a test to prevent an accidental change to the branch order.

💚 Proposed additional test
   it('unresolved, non-outdated thread with no other signal is also unresolved/null', () => {
     expect(classifyOutcome({ ...base, threadResolved: false, threadOutdated: false })).toEqual({
       outcome: 'unresolved',
       outcomeEvidence: null,
     });
   });
+
+  it('resolved with no line touch and no human reply stays unresolved/null', () => {
+    expect(classifyOutcome({ ...base, threadResolved: true })).toEqual({
+      outcome: 'unresolved',
+      outcomeEvidence: null,
+    });
+  });
+
+  it('resolved + outdated with no other signal reports outdated-only', () => {
+    expect(classifyOutcome({ ...base, threadResolved: true, threadOutdated: true })).toEqual({
+      outcome: 'unresolved',
+      outcomeEvidence: 'outdated-only',
+    });
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/__tests__/mine-bots.test.mts` around lines 184 - 193, Add
a test alongside the existing fallback cases in the classifyOutcome suite for
threadResolved: true, lineTouchedLater: false, and hasHumanReply: false,
asserting the result remains { outcome: 'unresolved', outcomeEvidence: null }
and preserves the current branch-order behavior.

85-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add assertions for the untested withdrawal alternatives.

WITHDRAWAL_RE has five alternatives. The suite exercises you're right, you are right, agreed, and agreed—. It does not exercise withdraw or does not apply. The test title at Line 85 also names the agreed- hyphen form but does not assert it. The regex decides the rebutted label, so full alternative coverage is worth the three extra lines.

💚 Proposed additional assertions
   it('matches the "agreed—" / "agreed," / "agreed-" withdrawal forms', () => {
     expect(
       hasWithdrawal([{ author: 'coderabbitai[bot]', body: 'Agreed, closing this out.' }]),
     ).toBe(true);
     expect(hasWithdrawal([{ author: 'coderabbitai[bot]', body: 'Agreed—no action needed.' }])).toBe(
       true,
     );
+    expect(hasWithdrawal([{ author: 'coderabbitai[bot]', body: 'Agreed- reverting.' }])).toBe(true);
+  });
+
+  it('matches the "withdraw" and "does not apply" forms', () => {
+    expect(
+      hasWithdrawal([{ author: 'coderabbitai[bot]', body: 'I withdraw this comment.' }]),
+    ).toBe(true);
+    expect(
+      hasWithdrawal([{ author: 'coderabbitai[bot]', body: 'This does not apply here.' }]),
+    ).toBe(true);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/__tests__/mine-bots.test.mts` around lines 85 - 92, Extend
the withdrawal-form test around hasWithdrawal to assert the missing
WITHDRAWAL_RE alternatives: the “agreed-” hyphen form, “withdraw,” and “does not
apply,” using matching bot-authored bodies and expecting true. Keep the existing
assertions and test title intact.
gate-engine/review/eval/reviewers/mine-bots.mts (1)

60-78: 🚀 Performance & Scalability | 🔵 Trivial

Consider an incremental filter to limit GitHub API volume.

listPrs returns every PR with state=all. The sweep then calls botComments for each PR, and fetchReviewThreads plus fetchPrCommits for each PR that has bot comments. commitFiles adds one more request per later commit. On a repository with many PRs this consumes a large share of the REST and GraphQL rate limits, and a secondary rate limit can abort the run mid-sweep.

The script already merges into existing candidates.jsonl rows. Add an optional --since or --updated-after filter, or skip PRs whose url set is already fully present in the merged map, so repeat sweeps only fetch new PRs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/eval/reviewers/mine-bots.mts` around lines 60 - 78, Reduce
repeat GitHub API requests in the sweep built around listPrs, botComments, and
the merged candidates map by adding an optional since/updated-after filter or
skipping PRs whose URLs are already fully represented in the merged map.
Preserve processing for new or incomplete PR entries while ensuring repeat
sweeps do not fetch comments and related data for already-complete candidates.
gate-engine/review/eval/reviewers/README.md (1)

122-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Complete the gateHash input list.

benchGateHash in gate-engine/review/eval/reviewers/corpus.mts also hashes runtime.mts and every file in SHARED_HELPERS. The list here omits both, so a reader cannot predict which edits invalidate a baseline. Add them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/eval/reviewers/README.md` around lines 122 - 124, Update
the gateHash input-list documentation in the reviewer README to include
runtime.mts and every file under SHARED_HELPERS, matching the inputs hashed by
benchGateHash in corpus.mts so baseline invalidation is fully documented.
gate-engine/review/__tests__/reviewer-eval.test.mts (1)

379-385: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix the truncated test name and give this case a distinct assertion.

The name ends mid-sentence: "…(whole-corpus corpusHash drift) does not". The assertion also repeats the case at lines 359-361, so it adds no coverage. Assert the intended property instead: compute two rowHash values for the same row before and after a sibling row is appended, then pair them.

🔧 Proposed fix
-  it('a sibling row appended elsewhere in the corpus (whole-corpus corpusHash drift) does not', () => {
+  it('a sibling row appended elsewhere in the corpus does not break pairing', () => {
     // Regression guard for the blocker: the stability rerun used to hard-gate on
     // `section.corpusHash === meta.corpusHash`, a row-SET hash that changes whenever ANY row is
     // added/removed/edited anywhere in the corpus — defeating row-level pairing entirely once the
     // corpus grew even by one row. rowUnchanged only ever looks at the two rows being paired.
-    expect(rowUnchanged({ rowHash: 'h1' }, 'h1')).toBe(true);
+    const row = goldRow();
+    const baseRow = { rowHash: rowHash(row) };
+    corpusHashFromRows([row, decoyRow()]); // corpus-set hash drifts; row pairing must not
+    expect(rowUnchanged(baseRow, rowHash(row))).toBe(true);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gate-engine/review/__tests__/reviewer-eval.test.mts` around lines 379 - 385,
Update the test case name to clearly describe that whole-corpus corpusHash drift
does not block pairing, and replace the duplicate rowUnchanged assertion with
coverage of the intended behavior: compute the same row’s rowHash before and
after appending a sibling row, then verify those values can be paired
successfully.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/benchmarks/benchmark-methodology.md`:
- Around line 103-106: Update the archive description in
docs/benchmarks/benchmark-methodology.md (lines 103-106) to state that the
fail-open 8 MiB cap and archive errors may leave reviewer FAILs with only
diff_sha256, making them telemetry-only and ineligible for replay-based row
minting. Regenerate docs/decisions/benchmarks-grow-from-telemetry.md (line 11)
with the same exception, then regenerate docs/decisions/INDEX.md (line 11) from
the corrected decision.
- Around line 42-44: Update the benchmark methodology’s “Blind-relabel” step to
define the fallback when a suite has fewer than 40 rows: audit all available
rows rather than selecting a 40–60 item subset, and report the resulting
low-power κ/α estimate alongside the results table.

In `@docs/decisions/benchmarks-grow-from-telemetry.md`:
- Line 17: Update the decision’s Scope entry to include
gate-engine/review/evidence/diff-archive.mts alongside the existing paths,
ensuring the FAIL diff-byte archive helper is covered by the path-based scope.

In `@gate-engine/review/__tests__/diff-archive.test.mts`:
- Around line 14-25: Remove the temporary directory created by mkdtempSync in
the archiveFailedDiff describe block during afterEach, using the existing dir
variable and recursive forced cleanup while preserving the environment-variable
restoration.

In `@gate-engine/review/eval/reviewers/bench.mts`:
- Around line 367-397: Track rows skipped by the row.stable === false check in
the comparison flow, pass the resulting unstable count into buildCompareReport
alongside changed and the other counts, and update buildCompareReport and its
report line in stats.mts to include unstable exclusions so reported
shared/paired totals match the flip table.
- Around line 672-678: Update the results loop in the failMode/abMode section to
default section.rows to an empty object before indexing by res.id. Match
compareReviewer’s existing section.rows ?? {} defensive behavior so sections
without rows are skipped without throwing.

In `@gate-engine/review/eval/reviewers/mine-bots.mts`:
- Around line 106-129: Guard the pagination loop around the cursor update in the
review-thread fetch flow: before continuing after hasNextPage, validate that
pageInfo.endCursor is non-null and differs from the current cursor; break when
it is missing or unchanged, otherwise assign it to cursor and continue fetching.
- Around line 93-95: Update the inner comments connection in fetchReviewThreads
to paginate beyond the first 50 entries, or explicitly detect and report when
comments are truncated. Ensure threadInfoFor receives all comments needed for
hasAddressedMarker, hasWithdrawal, hasHumanReply, byDatabaseId, threadResolved,
and threadOutdated to produce correct results.
- Around line 259-281: Update collectCorpusUrls to wrap each matching corpus
file’s readFileSync call in error handling, skipping unreadable files and
directory entries while continuing to process the remaining entries. Preserve
the existing malformed-line handling and URL collection behavior for readable
files.
- Around line 368-373: Update the scopeForPr call in the scope confirmation
block to pass the full repository slug for cache-key construction while
preserving repoShort for the database query and SQL matching. Ensure
repositories with identical short names but different owners use distinct
scopeCache entries, without changing scopeConfirmed or scopedReviewers behavior.
- Around line 420-431: Update the merge loop around readExistingCandidates so
only newRows entries with a truthy row.url are inserted, preventing missing URLs
from sharing one merge key. Write the serialized rows to a temporary candidates
file, then use renameSync to atomically replace OUT; add renameSync to the
existing node:fs import and preserve the current output contents.
- Around line 172-194: Update commitFiles to paginate the GitHub commit-files
API until all pages are retrieved, aggregating every filename before caching and
returning the result. Preserve the existing cache behavior and error fallback,
and ensure commitsAfter receives the complete file list so isLineTouchedLater
can detect paths on later pages.

In `@README.md`:
- Line 130: Update the dashboard image alt text in README.md to match the labels
and summary represented by dashboard-light.svg and dashboard-dark.svg, removing
the unsupported current-count claim. Use the exact generated summary wording if
the dashboard snapshot remains included.

---

Outside diff comments:
In `@gate-engine/review/eval/reviewers/bench.mts`:
- Around line 278-298: Align the cascade eligibility used by overturnRate and
rescueRate in the metrics-building return: update the rescueRate numerator over
decoyFirstFail to use the same r.escalateLive condition as overturnRate,
preserving the existing denominator unless the surrounding metric contract
requires otherwise.

---

Nitpick comments:
In `@gate-engine/review/__tests__/mine-bots.test.mts`:
- Around line 184-193: Add a test alongside the existing fallback cases in the
classifyOutcome suite for threadResolved: true, lineTouchedLater: false, and
hasHumanReply: false, asserting the result remains { outcome: 'unresolved',
outcomeEvidence: null } and preserves the current branch-order behavior.
- Around line 85-92: Extend the withdrawal-form test around hasWithdrawal to
assert the missing WITHDRAWAL_RE alternatives: the “agreed-” hyphen form,
“withdraw,” and “does not apply,” using matching bot-authored bodies and
expecting true. Keep the existing assertions and test title intact.

In `@gate-engine/review/__tests__/reviewer-eval.test.mts`:
- Around line 379-385: Update the test case name to clearly describe that
whole-corpus corpusHash drift does not block pairing, and replace the duplicate
rowUnchanged assertion with coverage of the intended behavior: compute the same
row’s rowHash before and after appending a sibling row, then verify those values
can be paired successfully.

In `@gate-engine/review/eval/reviewers/mine-bots-lib.mts`:
- Around line 61-69: Update hasWithdrawal to accept the same injectable
botAuthors set used by hasHumanReply, defaulting to the existing bot author when
omitted, and test membership against that set so withdrawals from both swept bot
accounts are recognized.
- Around line 105-114: Clarify the documentation for isLineTouchedLater to state
that it detects later file-level touches, not changes to the specific commented
lines, and that this is a deliberate approximation. Keep the existing
implementation and classifyOutcome behavior unchanged.

In `@gate-engine/review/eval/reviewers/mine-bots.mts`:
- Around line 60-78: Reduce repeat GitHub API requests in the sweep built around
listPrs, botComments, and the merged candidates map by adding an optional
since/updated-after filter or skipping PRs whose URLs are already fully
represented in the merged map. Preserve processing for new or incomplete PR
entries while ensuring repeat sweeps do not fetch comments and related data for
already-complete candidates.

In `@gate-engine/review/eval/reviewers/README.md`:
- Around line 122-124: Update the gateHash input-list documentation in the
reviewer README to include runtime.mts and every file under SHARED_HELPERS,
matching the inputs hashed by benchGateHash in corpus.mts so baseline
invalidation is fully documented.

In `@gate-engine/review/evidence/diff-archive.mts`:
- Around line 49-67: Update the diff archive flow around archiveFailedDiff to
enforce a retention policy for the diffs store, using periodic cleanup by entry
age or total directory size before or after archiving. Preserve hash-based
deduplication and best-effort failure handling, and ensure cleanup errors never
cause a gate to fail.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b0d95ba6-3f0f-4556-982a-52d146989403

📥 Commits

Reviewing files that changed from the base of the PR and between 4d8b27f and 565dafb.

⛔ Files ignored due to path filters (2)
  • docs/benchmarks/assets/dashboard-dark.svg is excluded by !**/*.svg
  • docs/benchmarks/assets/dashboard-light.svg is excluded by !**/*.svg
📒 Files selected for processing (17)
  • README.md
  • docs/benchmarks/README.md
  • docs/benchmarks/benchmark-methodology.md
  • docs/decisions/INDEX.md
  • docs/decisions/benchmarks-grow-from-telemetry.md
  • gate-engine/review/__tests__/diff-archive.test.mts
  • gate-engine/review/__tests__/mine-bots.test.mts
  • gate-engine/review/__tests__/reviewer-eval.test.mts
  • gate-engine/review/eval/reviewers/README.md
  • gate-engine/review/eval/reviewers/bench.mts
  • gate-engine/review/eval/reviewers/corpus.mts
  • gate-engine/review/eval/reviewers/mine-bots-lib.mts
  • gate-engine/review/eval/reviewers/mine-bots.mts
  • gate-engine/review/eval/reviewers/progress.mts
  • gate-engine/review/eval/reviewers/stats.mts
  • gate-engine/review/evidence/diff-archive.mts
  • gate-engine/review/run-review.mts

Comment thread docs/benchmarks/benchmark-methodology.md Outdated
Comment thread docs/benchmarks/benchmark-methodology.md Outdated
Comment thread docs/decisions/benchmarks-grow-from-telemetry.md
Comment thread gate-engine/review/__tests__/diff-archive.test.mts
Comment thread gate-engine/review/eval/reviewers/bench.mts
Comment thread gate-engine/review/eval/reviewers/mine-bots.mts
Comment thread gate-engine/review/eval/reviewers/mine-bots.mts
Comment thread gate-engine/review/eval/reviewers/mine-bots.mts
Comment thread gate-engine/review/eval/reviewers/mine-bots.mts Outdated
Comment thread README.md Outdated
…, FAIL-diff archive, miner outcome labels

PR A of the corpus-growth effort (charter: docs/benchmarks/benchmark-methodology.md,
decision: docs/decisions/benchmarks-grow-from-telemetry.md):

- corpusHash becomes a row-SET hash (sorted per-row canonical-JSON hashes); baselines,
  progress checkpoints, and the stability rerun pair per-row via rowHash, so appending
  corpus rows no longer invalidates comparisons or salvage over retained rows.
- compareReviewer pairs on the row-id intersection (changed rows excluded, added/removed
  counted) and reports pooled + gold-only + decoy-only + clustered-by-case flip tables
  (pure stats.mts); checkpoint/salvage/baseline IO split into progress.mts (size ratchet);
  optional caseId/sourcePr/outcomeEvidence/scopeConfirmed row fields with enum lint.
- summarize gains cascade-only overturnRate/rescueRate — the metric the July cascade
  regression was invisible without; sonnet recall floor now gates on effective models,
  not the env default.
- validate compiles reasonPattern (hard fail) and warns non-fatally on comment-leakage
  tells (pattern-vs-comment match, note-vs-comment Jaccard overlap).
- mine-bots: GraphQL thread resolution, CodeRabbit marker parsing, priority-ordered
  outcome + outcomeEvidence labels, per-PR line-touched checks, collector scope
  confirmation (scopeConfirmed/scopedReviewers), merge-by-url, alreadyInCorpus flags.
- archiveFailedDiff: content-addressed <telemetry>/diffs/<diff_sha256>.diff.gz on
  reviewer FAIL, fail-open, 8MiB cap, runReviewGate-only (bench can never archive);
  joins telemetry via the existing diff_sha256 — no event schema change.
- docs: living benchmark methodology (checklist + addendum) moved here from frink;
  tracker views re-rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- methodology: small-suite fallback for the 40-60 blind-relabel window;
  archive wording aligned with the fail-open/8MiB/telemetry-on contract.
- decision scope: diff-archive.mts added via guard-decisions rescope.
- bench: unstable-excluded rows reported in the compare line; section.rows
  guarded on the --against path.
- mine-bots: inner thread-comments pagination warning (first:100), hard page
  cap on the cursor loop, 300-file commit truncation surfaces as
  lineTouchedTruncated (never a false negative), corpus-url read guarded,
  scope cache keyed by full repo slug, atomic candidates.jsonl write +
  merge-key guard.
- diff-archive test: temp-dir cleanup in afterEach.

README alt-text finding rebutted in-thread: that section is generated by
benchmarks:render; the fix belongs in the generator, tracked as follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@norvalbv
norvalbv merged commit db87089 into main Aug 1, 2026
1 of 2 checks passed
norvalbv added a commit that referenced this pull request Aug 2, 2026
…from the collector db (#303)

* feat(reviewer-eval): mine-telemetry — fail→fix golds + waived decoys from the collector db

Capture point 1 of the corpus-growth charter, plus the historical override-valve
decoy slice. Read-side design: the miner SELECTs ~/.claude-usage/usage.db
(USAGE_DB override) and reads the #295 diff archive — the collector itself is
untouched (deviation from the addendum's 'collector correlates' phrasing,
recorded in the module docstring; read-side correlation adds zero write paths).

- fail→fix: per-(repo,branch) chronological chains (ISO-aware ordering, ship-id
  tiebreak); PER-LENS correlation when lens data exists, reviewer-level fallback
  only on lens-data absence; same-diff overrides route to the decoy path;
  fixed-by-absence recognized on clean ships. Candidate lenses are an ALLOWLIST
  on disposition ('blocking' or pre-disposition null). Merge keys carry the
  candidate kind. pickFailReason falls through to the reviewer reason when
  issues_json filters to nothing (whitespace-only arrays shadowed it via ??).
- Evidence honesty: failReason-only rows flagged + counted; repo allowlist
  defaults to devkit (frink diffs are private — explicit flag + adapt stage).
- Shared miner plumbing extracted to mine-common.mts (jscpd: zero clones).
- Atomic merge-by-key output to raw/candidates-telemetry.jsonl (gitignored);
  fail-open on missing db/sqlite3/archive; bypasses propose.mts by design.

Live dry-run (devkit scope): 44 candidates incl. 9 with archived bytes;
idempotent re-runs. 48 unit tests; 490/490 suite green; tsc/biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(reviewer-eval): address CodeRabbit review on #303

Three findings, all valid.

- selectFailLensRows (new, in the lib so it is testable): an empty blocking
  filter used to fall through to `[null]`, the "no lens breakdown recorded"
  branch. A fail whose only failing lenses were waived or dropped_out_of_charter
  therefore minted a reviewer-level gold from exactly the lenses the allowlist
  had just excluded — contradicting the rule the comment above it states. Now
  three distinct cases, and the skip is COUNTED in the drop histogram
  (fail-fix:all-failing-lenses-non-blocking) rather than vanishing silently,
  which is the same evidence-honesty bar as the rest of the funnel.

- collectRepoArgs: `--repo --dev` stored "--dev" as the repository. Both callers
  treat any non-empty result as an explicit scope replacing their defaults, so
  that silently narrowed the sweep to a repo that cannot exist and reported a
  clean run. A missing or flag-shaped value is now a usage error.

- sqliteJson: the docstring claimed read-only but nothing enforced it. Now opens
  the collector db with sqlite3 `-readonly`, so the boundary is held by the
  engine — a write raises "attempt to write a readonly database" instead of
  mutating the user's telemetry. The miners are strictly read-side.

Tests: 47 -> 61 in mine-telemetry.test.mts, covering all three cases of the lens
rule, both malformed --repo shapes, and a refused INSERT/DDL that leaves the db
intact. Full gate-engine/review suite 503/503.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
norvalbv added a commit that referenced this pull request Aug 2, 2026
…clone-gate ruling (#310)

benchmarks-grow-from-telemetry gains its convergence record before the release
that ships it: capture loop closed end-to-end (#295/#302/#303/#309, first 8
pure-telemetry rows, corpus 128), label-trust precondition met (#304: κ 0.735
post-triage, 4.2% noise floor; cleanlab floor still pending bench pred_probs),
and the Target's c-CRAB/CR-Bench known-answer path recorded as falsified
(#307) with the replacement candidates awaiting ratification.

New axis clone-gate-non-import-code ([VALIDATED]): clones are measured over
non-import code, excluded at the jscpd tokenizer — with the six-hole failure
of post-hoc fragment classification recorded as the rejected road so a future
simplifier can't silently re-vacuous the gate (#305/#308).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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