Skip to content

PAN-3377 - #3380

Merged
overdeck-agent[bot] merged 5 commits into
mainfrom
strike/pan-3377
Aug 1, 2026
Merged

PAN-3377#3380
overdeck-agent[bot] merged 5 commits into
mainfrom
strike/pan-3377

Conversation

@eltmon

@eltmon eltmon commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Issue: #3377

Summary by CodeRabbit

  • Bug Fixes

    • Prevented stale review results from overwriting newer review cycles or commit states.
    • Skipped restoration of outdated verdicts while preserving the current live review status.
    • Rejected verdicts backed by mismatched evidence commits.
    • Cleared obsolete infrastructure-failure details when a new review begins.
    • Limited review status notes to 500 characters.
  • Tests

    • Added regression coverage for stale verdicts, review-cycle transitions, restoration safeguards, and status-note handling.

panopticon-agent[bot] and others added 5 commits August 1, 2026 01:17
Issue: PAN-3377

Co-Authored-By: Claude <noreply@anthropic.com>
Issue: PAN-3377

Co-Authored-By: Claude <noreply@anthropic.com>
Issue: PAN-3377

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

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds review-cycle and commit-aware stale-verdict protection across status updates, pipeline restoration, journal reconciliation, and workspace fallback handling. It also clears superseded infrastructure-failure state when a new review starts and centralizes note truncation.

Changes

Review verdict freshness safeguards

Layer / File(s) Summary
Verdict guards and review dispatch
src/lib/review-verdict-guards.ts, src/lib/cloister/review-agent.ts, src/lib/cloister/__tests__/review-agent.test.ts
Terminal verdicts with mismatched evidence commits are rejected. New review dispatch clears prior infrastructure-failure fields.
Status update integration
src/lib/review-status-limits.ts, src/lib/review-status.ts, tests/unit/lib/review-status.test.ts
History notes use shared truncation. Stale verdict evidence preserves the current status and emits a notification.
Cycle-aware verdict reconciliation
src/lib/pan-dir/pipeline-verdict-merge.ts, src/lib/review-status-read.ts, src/lib/pan-dir/verdict-restore.ts, tests/unit/lib/pan-dir/verdict-restore.test.ts
Verdict merging, journal reconciliation, and restoration compare review cycles and commit anchors before applying snapshots.
Workspace fallback protection
src/lib/overdeck/review-status-record-sync.ts, tests/unit/lib/overdeck/review-status-record-sync-drain.test.ts
Superseded workspace fallbacks are logged, deleted, and excluded from journal projection.

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

Sequence Diagram(s)

sequenceDiagram
  participant LiveReview
  participant PipelineSnapshot
  participant VerdictMerge
  participant ReviewStatusStore
  LiveReview->>VerdictMerge: provide current review cycle and HEAD
  PipelineSnapshot->>VerdictMerge: provide snapshot cycle and HEAD
  VerdictMerge->>VerdictMerge: detect stale terminal verdict
  VerdictMerge->>ReviewStatusStore: preserve current status or apply valid verdict
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title contains only an issue identifier and does not describe the main change, which is anchoring review verdicts to the active review cycle. Replace the issue identifier with a concise description, such as "Anchor review verdicts to the active review cycle."
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 strike/pan-3377

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


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

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/lib/review-status-read.ts (1)

32-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate stale-check-and-warn pattern.

This block (compute staleVerdictSnapshotAgainstLiveCycle, log a warning with live/snapshot cycle and HEAD, then refuse) repeats near-verbatim in drainWorkspaceVerdictFallback and projectJournalStatus in src/lib/overdeck/review-status-record-sync.ts, and in restoreOneIssue in src/lib/pan-dir/verdict-restore.ts. Extract a shared helper in pipeline-verdict-merge.ts that performs the check and the warning, and call it from all four sites.

🤖 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 `@src/lib/review-status-read.ts` around lines 32 - 46, Extract the duplicated
stale-verdict detection and warning logic into a shared helper in
pipeline-verdict-merge.ts, including the staleVerdictSnapshotAgainstLiveCycle
check, contextual warning, and refusal result. Replace the inline blocks in the
current review-status read flow, drainWorkspaceVerdictFallback,
projectJournalStatus, and restoreOneIssue with calls to that helper, preserving
each caller’s existing return behavior.
src/lib/pan-dir/pipeline-verdict-merge.ts (1)

215-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate cycle-comparison block.

The cycle-vs-write-time comparison (journalCycle, fallbackCycle, fallbackWrittenMs, and the compound condition) is identical to the block in findFallbackVerdictConflicts at lines 258-266. Extract a shared helper, e.g. journalCycleSupersedesFallback(journal, fallback): boolean, and call it from both functions.

♻️ Proposed extraction
+function journalCycleSupersedesFallback(
+  journal: PanIssuePipelineRecord,
+  fallback: { updatedAt: string; pipeline: PipelineFields },
+): boolean {
+  const journalCycle = reviewCycleMs(fields(journal));
+  const fallbackCycle = reviewCycleMs(fallback.pipeline);
+  const fallbackWrittenMs = cycleMs(fallback.updatedAt);
+  return journalCycle !== undefined
+    && ((fallbackCycle !== undefined && journalCycle > fallbackCycle)
+      || (fallbackCycle === undefined && fallbackWrittenMs !== undefined && journalCycle > fallbackWrittenMs));
+}
+
 export function pipelineCoversFallbackVerdicts(
   journal: PanIssuePipelineRecord,
   fallback: { updatedAt: string; pipeline: PipelineFields },
 ): boolean {
-  const journalCycle = reviewCycleMs(fields(journal));
-  const fallbackCycle = reviewCycleMs(fallback.pipeline);
-  const fallbackWrittenMs = cycleMs(fallback.updatedAt);
-  if (
-    journalCycle !== undefined
-    && ((fallbackCycle !== undefined && journalCycle > fallbackCycle)
-      || (fallbackCycle === undefined && fallbackWrittenMs !== undefined && journalCycle > fallbackWrittenMs))
-  ) {
+  if (journalCycleSupersedesFallback(journal, fallback)) {
     return 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 `@src/lib/pan-dir/pipeline-verdict-merge.ts` around lines 215 - 222, Extract
the duplicated cycle-versus-write-time comparison into a shared helper such as
journalCycleSupersedesFallback(journal, fallback), including the journalCycle,
fallbackCycle, fallbackWrittenMs calculations and compound condition. Replace
the equivalent logic in both the current merge flow and
findFallbackVerdictConflicts with calls to the helper, preserving the existing
boolean behavior.
src/lib/overdeck/review-status-record-sync.ts (1)

475-490: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate stale-check-and-warn pattern.

This block repeats in projectJournalStatus below (lines 627-643), in resolveJournalReconciledReviewStatusSync (src/lib/review-status-read.ts lines 32-46), and in restoreOneIssue (src/lib/pan-dir/verdict-restore.ts lines 106-120). Extract a shared "check and warn" helper next to staleVerdictSnapshotAgainstLiveCycle in pipeline-verdict-merge.ts.

🤖 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 `@src/lib/overdeck/review-status-record-sync.ts` around lines 475 - 490,
Extract the repeated stale-snapshot detection, warning, and boolean-result
behavior into a shared helper beside staleVerdictSnapshotAgainstLiveCycle in
pipeline-verdict-merge.ts. Update projectJournalStatus and the corresponding
flows in resolveJournalReconciledReviewStatusSync and restoreOneIssue to call
the helper, preserving each caller’s existing cleanup and return behavior while
removing duplicated logging logic.
src/lib/pan-dir/verdict-restore.ts (1)

106-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate stale-check-and-warn pattern.

This mirrors the same compute-and-warn block flagged in src/lib/review-status-read.ts (lines 32-46) and src/lib/overdeck/review-status-record-sync.ts (lines 475-490 and 627-643). A shared helper in pipeline-verdict-merge.ts would remove the repetition and keep the warning format from drifting across sites.

🤖 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 `@src/lib/pan-dir/verdict-restore.ts` around lines 106 - 120, The stale
snapshot computation and warning in the verdict restore flow duplicates logic
from review-status-read.ts and review-status-record-sync.ts. Extract or reuse a
shared helper in pipeline-verdict-merge.ts that performs the stale-check and
preserves the existing warning format, then update the relevant call sites
including the block around staleVerdictSnapshotAgainstLiveCycle and remove their
duplicated formatting logic.
🤖 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 `@src/lib/pan-dir/pipeline-verdict-merge.ts`:
- Around line 95-135: Update liveCycleActive in
staleVerdictSnapshotAgainstLiveCycle to recognize any live review status with an
unconsumed request, not only 'pending': retain 'reviewing' as active and treat
terminal or other statuses as active when liveRequested is defined and is newer
than liveSpawned (or liveSpawned is undefined). Preserve the existing cycle and
terminal-snapshot checks.

In `@src/lib/review-verdict-guards.ts`:
- Around line 38-67: The reviewer evidence validation in
findVerdictEvidenceHeadMismatch currently checks only reviewerEvidenceHeads[0].
Update this function to compare every collected atCommit value against
status.lastVerifiedCommit, while retaining reviewedAtCommit as the primary
anchor when it is present and preserving the existing mismatch result shape and
terminal-review conditions.

---

Nitpick comments:
In `@src/lib/overdeck/review-status-record-sync.ts`:
- Around line 475-490: Extract the repeated stale-snapshot detection, warning,
and boolean-result behavior into a shared helper beside
staleVerdictSnapshotAgainstLiveCycle in pipeline-verdict-merge.ts. Update
projectJournalStatus and the corresponding flows in
resolveJournalReconciledReviewStatusSync and restoreOneIssue to call the helper,
preserving each caller’s existing cleanup and return behavior while removing
duplicated logging logic.

In `@src/lib/pan-dir/pipeline-verdict-merge.ts`:
- Around line 215-222: Extract the duplicated cycle-versus-write-time comparison
into a shared helper such as journalCycleSupersedesFallback(journal, fallback),
including the journalCycle, fallbackCycle, fallbackWrittenMs calculations and
compound condition. Replace the equivalent logic in both the current merge flow
and findFallbackVerdictConflicts with calls to the helper, preserving the
existing boolean behavior.

In `@src/lib/pan-dir/verdict-restore.ts`:
- Around line 106-120: The stale snapshot computation and warning in the verdict
restore flow duplicates logic from review-status-read.ts and
review-status-record-sync.ts. Extract or reuse a shared helper in
pipeline-verdict-merge.ts that performs the stale-check and preserves the
existing warning format, then update the relevant call sites including the block
around staleVerdictSnapshotAgainstLiveCycle and remove their duplicated
formatting logic.

In `@src/lib/review-status-read.ts`:
- Around line 32-46: Extract the duplicated stale-verdict detection and warning
logic into a shared helper in pipeline-verdict-merge.ts, including the
staleVerdictSnapshotAgainstLiveCycle check, contextual warning, and refusal
result. Replace the inline blocks in the current review-status read flow,
drainWorkspaceVerdictFallback, projectJournalStatus, and restoreOneIssue with
calls to that helper, preserving each caller’s existing return behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b066eb7-2d64-47ee-a252-6272e7b29a74

📥 Commits

Reviewing files that changed from the base of the PR and between 85daed3 and 047c21f.

📒 Files selected for processing (12)
  • src/lib/cloister/__tests__/review-agent.test.ts
  • src/lib/cloister/review-agent.ts
  • src/lib/overdeck/review-status-record-sync.ts
  • src/lib/pan-dir/pipeline-verdict-merge.ts
  • src/lib/pan-dir/verdict-restore.ts
  • src/lib/review-status-limits.ts
  • src/lib/review-status-read.ts
  • src/lib/review-status.ts
  • src/lib/review-verdict-guards.ts
  • tests/unit/lib/overdeck/review-status-record-sync-drain.test.ts
  • tests/unit/lib/pan-dir/verdict-restore.test.ts
  • tests/unit/lib/review-status.test.ts

Comment on lines +95 to +135
export function staleVerdictSnapshotAgainstLiveCycle(
live: PipelineFields,
snapshot: PipelineFields,
): StaleVerdictSnapshot | null {
const liveReviewStatus = live.reviewStatus;
const liveSpawned = cycleMs(live.reviewSpawnedAt);
const liveRequested = cycleMs(live.reviewRequestedAt);
const liveCycle = reviewCycleMs(live);
const snapshotCycle = reviewCycleMs(snapshot);
const liveCycleActive = liveReviewStatus === 'reviewing'
|| (liveReviewStatus === 'pending'
&& liveRequested !== undefined
&& (liveSpawned === undefined || liveRequested > liveSpawned));

if (
!liveCycleActive
|| liveCycle === undefined
|| snapshotCycle === undefined
|| !carriesTerminalVerdict(snapshot)
|| snapshotCycle >= liveCycle
) return null;

return {
liveCycle,
snapshotCycle,
liveHead: typeof live.prHeadSha === 'string'
? live.prHeadSha
: typeof live.lastVerifiedCommit === 'string'
? live.lastVerifiedCommit
: typeof live.reviewedAtCommit === 'string'
? live.reviewedAtCommit
: undefined,
snapshotHead: typeof snapshot.reviewedAtCommit === 'string'
? snapshot.reviewedAtCommit
: typeof snapshot.lastVerifiedCommit === 'string'
? snapshot.lastVerifiedCommit
: typeof snapshot.prHeadSha === 'string'
? snapshot.prHeadSha
: undefined,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

liveCycleActive misses a terminal review status with an unconsumed newer request.

liveCycleActive treats only 'reviewing' or 'pending' (with reviewRequestedAt > reviewSpawnedAt) as an active cycle. setReviewStatusSync explicitly preserves reviewRequestedAt on a terminal transition when the request arrived after the spawn, to represent "an explicit re-review requested while the current review was running." That means a live row can carry a terminal reviewStatus ('blocked', 'failed', 'passed', 'skipped') together with a reviewRequestedAt newer than reviewSpawnedAt, and liveCycleActive does not recognize this as an active cycle.

In that state, staleVerdictSnapshotAgainstLiveCycle returns null even when reviewCycleMs(live) (driven by the newer reviewRequestedAt) exceeds the snapshot's cycle. An older terminal snapshot (from readJournalStatusSync, drainWorkspaceVerdictFallback, or restoreOneIssue) can then overlay stale reviewNotes/reviewedAtCommit fields onto a row that already recorded a newer review obligation.

Broaden the check to any status with an unconsumed request, not only 'pending'.
As per coding guidelines, "Fix broken behavior at its root cause; never add workarounds, hacks, fallback chains, or downstream defensive handling that merely masks symptoms" — fixing this in the shared function protects all three call sites at once.

🐛 Proposed fix for `liveCycleActive`
   const liveCycleActive = liveReviewStatus === 'reviewing'
-    || (liveReviewStatus === 'pending'
-      && liveRequested !== undefined
+    || (liveRequested !== undefined
       && (liveSpawned === undefined || liveRequested > liveSpawned));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function staleVerdictSnapshotAgainstLiveCycle(
live: PipelineFields,
snapshot: PipelineFields,
): StaleVerdictSnapshot | null {
const liveReviewStatus = live.reviewStatus;
const liveSpawned = cycleMs(live.reviewSpawnedAt);
const liveRequested = cycleMs(live.reviewRequestedAt);
const liveCycle = reviewCycleMs(live);
const snapshotCycle = reviewCycleMs(snapshot);
const liveCycleActive = liveReviewStatus === 'reviewing'
|| (liveReviewStatus === 'pending'
&& liveRequested !== undefined
&& (liveSpawned === undefined || liveRequested > liveSpawned));
if (
!liveCycleActive
|| liveCycle === undefined
|| snapshotCycle === undefined
|| !carriesTerminalVerdict(snapshot)
|| snapshotCycle >= liveCycle
) return null;
return {
liveCycle,
snapshotCycle,
liveHead: typeof live.prHeadSha === 'string'
? live.prHeadSha
: typeof live.lastVerifiedCommit === 'string'
? live.lastVerifiedCommit
: typeof live.reviewedAtCommit === 'string'
? live.reviewedAtCommit
: undefined,
snapshotHead: typeof snapshot.reviewedAtCommit === 'string'
? snapshot.reviewedAtCommit
: typeof snapshot.lastVerifiedCommit === 'string'
? snapshot.lastVerifiedCommit
: typeof snapshot.prHeadSha === 'string'
? snapshot.prHeadSha
: undefined,
};
}
export function staleVerdictSnapshotAgainstLiveCycle(
live: PipelineFields,
snapshot: PipelineFields,
): StaleVerdictSnapshot | null {
const liveReviewStatus = live.reviewStatus;
const liveSpawned = cycleMs(live.reviewSpawnedAt);
const liveRequested = cycleMs(live.reviewRequestedAt);
const liveCycle = reviewCycleMs(live);
const snapshotCycle = reviewCycleMs(snapshot);
const liveCycleActive = liveReviewStatus === 'reviewing'
|| (liveRequested !== undefined
&& (liveSpawned === undefined || liveRequested > liveSpawned));
if (
!liveCycleActive
|| liveCycle === undefined
|| snapshotCycle === undefined
|| !carriesTerminalVerdict(snapshot)
|| snapshotCycle >= liveCycle
) return null;
return {
liveCycle,
snapshotCycle,
liveHead: typeof live.prHeadSha === 'string'
? live.prHeadSha
: typeof live.lastVerifiedCommit === 'string'
? live.lastVerifiedCommit
: typeof live.reviewedAtCommit === 'string'
? live.reviewedAtCommit
: undefined,
snapshotHead: typeof snapshot.reviewedAtCommit === 'string'
? snapshot.reviewedAtCommit
: typeof snapshot.lastVerifiedCommit === 'string'
? snapshot.lastVerifiedCommit
: typeof snapshot.prHeadSha === 'string'
? snapshot.prHeadSha
: undefined,
};
}
🤖 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 `@src/lib/pan-dir/pipeline-verdict-merge.ts` around lines 95 - 135, Update
liveCycleActive in staleVerdictSnapshotAgainstLiveCycle to recognize any live
review status with an unconsumed request, not only 'pending': retain 'reviewing'
as active and treat terminal or other statuses as active when liveRequested is
defined and is newer than liveSpawned (or liveSpawned is undefined). Preserve
the existing cycle and terminal-snapshot checks.

Source: Coding guidelines

Comment on lines +38 to +67
function findVerdictEvidenceHeadMismatch(
status: ReviewGuardStatus,
update: ReviewGuardUpdate,
): VerdictEvidenceHeadMismatch | null {
const terminalReview = update.reviewStatus !== undefined
&& ['passed', 'blocked', 'failed', 'skipped'].includes(update.reviewStatus);
const reviewerEvidenceHeads = Object.values(update.reviewerVerdicts ?? {})
.flatMap((verdict) => verdict?.atCommit ? [verdict.atCommit] : []);
const reviewEvidenceHead = update.reviewedAtCommit ?? reviewerEvidenceHeads[0];
if (
terminalReview
&& reviewEvidenceHead
&& status.lastVerifiedCommit
&& reviewEvidenceHead !== status.lastVerifiedCommit
) {
return { gate: 'review', evidenceHead: reviewEvidenceHead, targetHead: status.lastVerifiedCommit };
}

const terminalTest = update.testStatus !== undefined
&& ['passed', 'failed'].includes(update.testStatus);
if (
terminalTest
&& update.lastVerifiedCommit
&& status.reviewedAtCommit
&& update.lastVerifiedCommit !== status.reviewedAtCommit
) {
return { gate: 'test', evidenceHead: update.lastVerifiedCommit, targetHead: status.reviewedAtCommit };
}
return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "reviewerVerdicts" src -g '*.ts' -C3

Repository: eltmon/overdeck

Length of output: 16421


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== review-verdict-guards outline =="
ast-grep outline src/lib/review-verdict-guards.ts --view expanded || true

echo "== review-verdict-guards relevant section =="
sed -n '1,140p' src/lib/review-verdict-guards.ts

echo "== review-status reconcile relevant section =="
sed -n '220,270p' src/lib/review-status.ts
sed -n '433,480p' src/lib/__tests__/review-status-reconcile.test.ts

echo "== tests mentioning findVerdictEvidenceHeadMismatch / lastVerifiedCommit =="
rg -n "findVerdictEvidenceHeadMismatch|lastVerifiedCommit|reviewerVerdicts" src/lib src/cli src/lib/__tests__ -g '*.ts' -C 2

Repository: eltmon/overdeck

Length of output: 43823


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== find call sites of rejectVerdictEvidenceHeadMismatch =="
rg -n "rejectVerdictEvidenceHeadMismatch" src -g '*.ts' -C 4

echo "== workspace-anchor-drift guard context =="
sed -n '1,220p' src/lib/workspace-anchor-drift.ts

Repository: eltmon/overdeck

Length of output: 7855


Check every reviewer evidence head, not only the first one.

Partial reviewerVerdicts updates merge with carried-forward entries, and partial anchor updates can omit reviewedAtCommit. When reviewerEvidenceHeads has multiple entries, reviewEvidenceHead only uses [0], so stale sub-reviewer verdict anchors beyond index 0 can pass this gate undetected. Compare the full set of atCommit values against status.lastVerifiedCommit.

🤖 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 `@src/lib/review-verdict-guards.ts` around lines 38 - 67, The reviewer evidence
validation in findVerdictEvidenceHeadMismatch currently checks only
reviewerEvidenceHeads[0]. Update this function to compare every collected
atCommit value against status.lastVerifiedCommit, while retaining
reviewedAtCommit as the primary anchor when it is present and preserving the
existing mismatch result shape and terminal-review conditions.

@overdeck-agent
overdeck-agent Bot merged commit 3497611 into main Aug 1, 2026
11 checks passed
This was referenced Aug 3, 2026
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