diff --git a/scripts/general-server-shard-durations.json b/scripts/general-server-shard-durations.json index 539cfd29b29..4aecdb41d78 100644 --- a/scripts/general-server-shard-durations.json +++ b/scripts/general-server-shard-durations.json @@ -1,5 +1,5 @@ { - "$comment": "Per-suite Vitest durations (ms) for the general-server lane, used by scripts/general-server-shard.mjs to balance suites across the PR shard matrix. Sampled from four successful ARC PR jobs in run 31248977534 on 2026-08-08, then topped up as master added suites, most recently on 2026-08-24 via scripts/measure-general-server-shard-durations.mjs; coverage is 443/443. This sentence is regenerated on every refresh; see \"$notes\" for guidance that is meant to persist.", + "$comment": "Per-suite Vitest durations (ms) for the general-server lane, used by scripts/general-server-shard.mjs to balance suites across the PR shard matrix. Sampled from four successful ARC PR jobs in run 31248977534 on 2026-08-08, then topped up as master added suites, most recently on 2026-09-04 via scripts/measure-general-server-shard-durations.mjs; coverage is 483/483. This sentence is regenerated on every refresh; see \"$notes\" for guidance that is meant to persist.", "$notes": "DURABLE NOTES -- unlike \"$comment\" (regenerated on every refresh), this key is never rewritten by .github/workflows/refresh-shard-manifest.yml. Put guidance that must outlive a re-sample here. NOTE ON UNITS: these are per-file TEST-EXECUTION durations (the Vitest JSON reporter's testResults[].startTime->endTime), not full wall-clock -- they exclude each file's transform/setup/import cost, which is why a trivial suite can read as single-digit ms. That fixed per-file cost is roughly uniform and the LPT partition also lands near-equal file counts per shard, so it does not skew balance; do not 'fix' a small value here by hand. Suites absent from this manifest use the median fallback (defaultSuiteWeight in scripts/general-server-shard.mjs); the refresh workflow re-samples weekly so refresh does not depend on a human noticing drift.", "unit": "ms", "durations": { @@ -11,18 +11,21 @@ "server/src/__tests__/acpx-local-execute.test.ts": 3547, "server/src/__tests__/acpx-local-skill-sync.test.ts": 52, "server/src/__tests__/activity-log-responsible-user.test.ts": 4715, + "server/src/__tests__/activity-log-transactional-publish.test.ts": 575, "server/src/__tests__/activity-service.test.ts": 10397, "server/src/__tests__/adapter-models.test.ts": 128, "server/src/__tests__/adapter-registry.test.ts": 30, "server/src/__tests__/adapter-session-codecs.test.ts": 29, "server/src/__tests__/agent-auth-jwt.test.ts": 134, "server/src/__tests__/agent-auth-middleware.test.ts": 96, + "server/src/__tests__/agent-budget-mirror-write.test.ts": 35, "server/src/__tests__/agent-hires-instructions-materialize.test.ts": 7694, "server/src/__tests__/agent-image-bump.test.ts": 57393, "server/src/__tests__/agent-inbox-lite-status-contract.test.ts": 11, "server/src/__tests__/agent-instructions-service.test.ts": 595, "server/src/__tests__/agent-invokability.test.ts": 14, "server/src/__tests__/agent-permissions-service.test.ts": 18, + "server/src/__tests__/agent-profile-change-gate-mixing.test.ts": 1717, "server/src/__tests__/agent-run-health.test.ts": 9, "server/src/__tests__/agent-scorecards.test.ts": 22, "server/src/__tests__/agent-secret-redaction.test.ts": 358, @@ -45,6 +48,7 @@ "server/src/__tests__/approval-insert.test.ts": 10, "server/src/__tests__/approval-linked-agent-migration.test.ts": 15122, "server/src/__tests__/approval-payload-title-guard.test.ts": 4684, + "server/src/__tests__/approval-revision-exit.test.ts": 1484, "server/src/__tests__/approval-withdraw-plugin-event.test.ts": 9363, "server/src/__tests__/approvals-service.test.ts": 4562, "server/src/__tests__/attachment-types.test.ts": 12, @@ -94,6 +98,8 @@ "server/src/__tests__/company-skills.test.ts": 106, "server/src/__tests__/config-pr-reviewer-pool.test.ts": 40, "server/src/__tests__/config-recovery-action-bounds.test.ts": 21, + "server/src/__tests__/crash-guard-install-wiring.test.ts": 1, + "server/src/__tests__/crash-run-marking.test.ts": 108, "server/src/__tests__/cursor-local-adapter-environment.test.ts": 530, "server/src/__tests__/cursor-local-adapter.test.ts": 29, "server/src/__tests__/cursor-local-execute.test.ts": 2579, @@ -140,6 +146,7 @@ "server/src/__tests__/execution-workspaces-derive-agent-cwd.test.ts": 4, "server/src/__tests__/execution-workspaces-service.test.ts": 49219, "server/src/__tests__/external-objects-service.test.ts": 11944, + "server/src/__tests__/external-wait-doc-contract.test.ts": 3, "server/src/__tests__/feedback-flush-controller.test.ts": 14, "server/src/__tests__/feedback-service.test.ts": 5336, "server/src/__tests__/feedback-share-client.test.ts": 23, @@ -154,6 +161,8 @@ "server/src/__tests__/git-checkout-identity.test.ts": 18930, "server/src/__tests__/git-worktree-ownership.test.ts": 4170, "server/src/__tests__/github-app-auth.test.ts": 219, + "server/src/__tests__/github-review-gate-authority.test.ts": 166, + "server/src/__tests__/github-review-posted-metric.test.ts": 10, "server/src/__tests__/github-status-delivery-outbox.test.ts": 11013, "server/src/__tests__/github-webhook.test.ts": 47153, "server/src/__tests__/google-sheets-gallery.test.ts": 12, @@ -170,7 +179,7 @@ "server/src/__tests__/heartbeat-auto-checkout.test.ts": 7, "server/src/__tests__/heartbeat-ccrotate-capacity-retry.test.ts": 13324, "server/src/__tests__/heartbeat-comment-wake-batching.test.ts": 22996, - "server/src/__tests__/heartbeat-context-summary.test.ts": 54, + "server/src/__tests__/heartbeat-context-summary.test.ts": 7485, "server/src/__tests__/heartbeat-cooldown.test.ts": 12, "server/src/__tests__/heartbeat-cost-accounting.test.ts": 9, "server/src/__tests__/heartbeat-dependabot-stale-wake-backfill.test.ts": 12121, @@ -200,11 +209,13 @@ "server/src/__tests__/heartbeat-project-env.test.ts": 109, "server/src/__tests__/heartbeat-provider-capacity-horizon.test.ts": 10247, "server/src/__tests__/heartbeat-queued-backlog-convergence.test.ts": 184313, + "server/src/__tests__/heartbeat-quota-recovery-wake-scope.test.ts": 2527, "server/src/__tests__/heartbeat-rate-limit-exhausted.test.ts": 8401, "server/src/__tests__/heartbeat-rate-limit-retry-schedule.test.ts": 13, "server/src/__tests__/heartbeat-recoverable-error-family.test.ts": 8, "server/src/__tests__/heartbeat-responsible-user-invariant.test.ts": 24618, "server/src/__tests__/heartbeat-retry-scheduling.test.ts": 47427, + "server/src/__tests__/heartbeat-retry-successor.test.ts": 2588, "server/src/__tests__/heartbeat-run-log.test.ts": 12, "server/src/__tests__/heartbeat-run-scoped-job-liveness-dispatch.test.ts": 9644, "server/src/__tests__/heartbeat-run-summary.test.ts": 18, @@ -220,8 +231,10 @@ "server/src/__tests__/heartbeat-timeout-outcome.test.ts": 6, "server/src/__tests__/heartbeat-timer-wake-coalescing.test.ts": 4980, "server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts": 18, + "server/src/__tests__/heartbeat-unmaterialized-desired-skills.test.ts": 2, "server/src/__tests__/heartbeat-wake-dispatch-retry.test.ts": 29126, "server/src/__tests__/heartbeat-wake-terminal-failed-gauge.test.ts": 17309, + "server/src/__tests__/heartbeat-worker-crash-marking.test.ts": 931, "server/src/__tests__/heartbeat-workspace-branch-containment.test.ts": 15513, "server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts": 17883, "server/src/__tests__/heartbeat-workspace-session.test.ts": 1855, @@ -232,7 +245,11 @@ "server/src/__tests__/helpers/truncate-company-scoped-test-state.test.ts": 2250, "server/src/__tests__/hire-hook.test.ts": 21, "server/src/__tests__/http-log-policy.test.ts": 11, + "server/src/__tests__/human-gated-ageing-digest.test.ts": 2180, "server/src/__tests__/human-gated-ageing.test.ts": 85, + "server/src/__tests__/human-gated-gate-revalidation-backfill.test.ts": 18, + "server/src/__tests__/human-gated-gate-revalidation-wiring.test.ts": 259, + "server/src/__tests__/human-gated-gate-revalidation.test.ts": 8, "server/src/__tests__/in-review-gate.test.ts": 10, "server/src/__tests__/inbox-dismissals.test.ts": 8466, "server/src/__tests__/instance-settings-service.test.ts": 45, @@ -253,6 +270,7 @@ "server/src/__tests__/issue-execution-policy.test.ts": 50, "server/src/__tests__/issue-force-release.test.ts": 182, "server/src/__tests__/issue-goal-fallback.test.ts": 10, + "server/src/__tests__/issue-list-scheduled-retry-projection.test.ts": 360, "server/src/__tests__/issue-liveness.test.ts": 43, "server/src/__tests__/issue-monitor-convergence-guard.test.ts": 114, "server/src/__tests__/issue-monitor-convergence-message.test.ts": 9, @@ -269,18 +287,22 @@ "server/src/__tests__/issue-thread-interactions-telemetry.test.ts": 5130, "server/src/__tests__/issue-tree-control-service-unit.test.ts": 14, "server/src/__tests__/issue-tree-control-service.test.ts": 11269, + "server/src/__tests__/issues-alertmanager-aggregate-create.test.ts": 288, "server/src/__tests__/issues-getByIdentifier-legacy-fallback.test.ts": 5120, "server/src/__tests__/issues-getByLinearIssueId.test.ts": 6892, "server/src/__tests__/issues-identifier-provider.test.ts": 5175, "server/src/__tests__/issues-last-activity-at.test.ts": 20223, + "server/src/__tests__/issues-list-page-param-rejection.test.ts": 21, "server/src/__tests__/issues-list-query-parsing.test.ts": 62, "server/src/__tests__/issues-open-assignment-census.test.ts": 39821, "server/src/__tests__/issues-patch-evidence.test.ts": 7008, + "server/src/__tests__/issues-plugin-fencing-generation.test.ts": 2888, "server/src/__tests__/issues-user-context.test.ts": 11, "server/src/__tests__/join-request-dedupe.test.ts": 10, "server/src/__tests__/json-schema-secret-refs.test.ts": 6, "server/src/__tests__/k8s-job-liveness-run-scoped.test.ts": 630, "server/src/__tests__/k8s-job-liveness.test.ts": 37, + "server/src/__tests__/lapsed-monitor-grace-bound.test.ts": 8556, "server/src/__tests__/lifecycle-hook-command-audit.test.ts": 535, "server/src/__tests__/linear-webhook-fixture-replay.test.ts": 4757, "server/src/__tests__/linear-webhook-fixtures.test.ts": 10, @@ -288,21 +310,26 @@ "server/src/__tests__/live-events-ws.test.ts": 45, "server/src/__tests__/log-redaction.test.ts": 14, "server/src/__tests__/logger-tz.test.ts": 58, + "server/src/__tests__/managed-checkout-partial-clone.test.ts": 1583, "server/src/__tests__/mcp-http.test.ts": 18, + "server/src/__tests__/mcp-seed-scrub-coverage.test.ts": 6, "server/src/__tests__/metrics-ccrotate-capacity-deferred.test.ts": 17, "server/src/__tests__/metrics-service.test.ts": 269, "server/src/__tests__/monthly-spend-service.test.ts": 74, "server/src/__tests__/normalize-agent-mention-token.test.ts": 6, + "server/src/__tests__/numeric-env-bounds.test.ts": 95119, "server/src/__tests__/oidc-rbac.test.ts": 26, "server/src/__tests__/openclaw-gateway-adapter.test.ts": 148, "server/src/__tests__/opencode-k8s-seed-transport.test.ts": 17886, "server/src/__tests__/opencode-local-adapter.test.ts": 20, "server/src/__tests__/opencode-local-skill-sync.test.ts": 55, + "server/src/__tests__/orphaned-run-terminal-result.test.ts": 12, "server/src/__tests__/overdue-scheduled-retry-metrics.test.ts": 2730, "server/src/__tests__/paperclip-env.test.ts": 9, "server/src/__tests__/paperclip-node-role.test.ts": 126, "server/src/__tests__/paperclip-skill-utils.test.ts": 66, "server/src/__tests__/parse-status-filter.test.ts": 15, + "server/src/__tests__/pen2370-mirror-redaction.test.ts": 52, "server/src/__tests__/penstock-availability-gate.test.ts": 41, "server/src/__tests__/pi-local-adapter-environment.test.ts": 360, "server/src/__tests__/pi-local-execute.test.ts": 3016, @@ -319,6 +346,7 @@ "server/src/__tests__/plugin-dev-watcher.test.ts": 84, "server/src/__tests__/plugin-environment-driver-seam.test.ts": 46, "server/src/__tests__/plugin-event-outbox.test.ts": 7681, + "server/src/__tests__/plugin-events-ownership-check.test.ts": 25, "server/src/__tests__/plugin-execution-workspace-bridge.test.ts": 17, "server/src/__tests__/plugin-install-autobuild.test.ts": 13114, "server/src/__tests__/plugin-isolated-store-migration.test.ts": 235, @@ -339,6 +367,7 @@ "server/src/__tests__/plugin-tool-dispatcher-dbid.test.ts": 15, "server/src/__tests__/plugin-tool-dispatcher-pluginDbId.test.ts": 26, "server/src/__tests__/plugin-ui-static.test.ts": 2735, + "server/src/__tests__/plugin-webhook-not-ready-retryable.test.ts": 42, "server/src/__tests__/plugin-webhook-verification.test.ts": 2775, "server/src/__tests__/plugin-worker-manager.test.ts": 1094, "server/src/__tests__/pr-comment-review-gate-check.test.ts": 271, @@ -346,7 +375,9 @@ "server/src/__tests__/pr-reconciler-sweep.test.ts": 18, "server/src/__tests__/pr-review-gate-status-target.test.ts": 17, "server/src/__tests__/pr-review-issue-scope-locks.test.ts": 2026, + "server/src/__tests__/pr-review-request-ageing-digest.test.ts": 191, "server/src/__tests__/pr-review-request-ageing.test.ts": 34, + "server/src/__tests__/pr-review-state-reconciler.test.ts": 131, "server/src/__tests__/private-hostname-guard.test.ts": 81, "server/src/__tests__/process-crash-guard-exit.test.ts": 2859, "server/src/__tests__/process-crash-guard.test.ts": 409, @@ -369,6 +400,7 @@ "server/src/__tests__/redact-sensitive.test.ts": 11, "server/src/__tests__/remote-http-endpoint-guard.test.ts": 30, "server/src/__tests__/routine-run-telemetry.test.ts": 7148, + "server/src/__tests__/routine-scoped-retry.test.ts": 3, "server/src/__tests__/routines-service.test.ts": 15140, "server/src/__tests__/run-continuations.test.ts": 13, "server/src/__tests__/run-liveness.test.ts": 16, @@ -390,6 +422,7 @@ "server/src/__tests__/static-index-html.test.ts": 92, "server/src/__tests__/storage-local-provider.test.ts": 74, "server/src/__tests__/stranded-blocked-issue-reconciler.test.ts": 3747, + "server/src/__tests__/stranded-recovery-drain-metering.test.ts": 1, "server/src/__tests__/stranded-run-recovery.test.ts": 8, "server/src/__tests__/successful-run-handoff-liveness.test.ts": 817, "server/src/__tests__/summary-slots.test.ts": 7447, @@ -405,6 +438,7 @@ "server/src/__tests__/tool-gateway-service.test.ts": 12489, "server/src/__tests__/tool-gateway.test.ts": 32320, "server/src/__tests__/tool-oauth-legacy-backfill.test.ts": 8289, + "server/src/__tests__/transient-retry-floor-jitter.test.ts": 1992, "server/src/__tests__/trust-preset-resolver.test.ts": 17, "server/src/__tests__/trust-proxy.test.ts": 27, "server/src/__tests__/ui-branding.test.ts": 8, @@ -434,12 +468,15 @@ "server/src/services/gbrain-client-factory.test.ts": 51, "server/src/services/heartbeat-run-runtime-status.test.ts": 48, "server/src/services/heartbeat-stop-metadata.test.ts": 15, + "server/src/services/issue-comment-effects.test.ts": 755, "server/src/services/issue-thread-interactions.test.ts": 2107, "server/src/services/recovery/model-profile-hint.test.ts": 11, + "server/src/services/recovery/origins.test.ts": 2, "server/src/services/recovery/provider-failure-classification.test.ts": 17, "server/src/services/recovery/service.infra-class-continuation.test.ts": 7, "server/src/services/recovery/service.pause-durability.test.ts": 6, "server/src/services/recovery/strand-comment-provider-capacity.test.ts": 47, + "server/src/services/recovery/stranded-escalation-status.test.ts": 2, "server/src/services/recovery/successful-run-handoff.test.ts": 16, "server/src/services/responsible-user-denial-run-outcomes.test.ts": 16, "server/src/services/routines-formatter-cache.test.ts": 2287, @@ -447,6 +484,7 @@ "server/src/services/run-scratch.test.ts": 35, "server/src/services/session-workspace-cwd.test.ts": 14, "server/src/services/sweep-wake-preflight.test.ts": 24, + "server/src/services/wake-idempotency.test.ts": 107, "server/src/shutdown.test.ts": 67 } } diff --git a/server/src/__tests__/heartbeat-context-summary.test.ts b/server/src/__tests__/heartbeat-context-summary.test.ts index 2fd5bff54b0..04c78ec7817 100644 --- a/server/src/__tests__/heartbeat-context-summary.test.ts +++ b/server/src/__tests__/heartbeat-context-summary.test.ts @@ -4,6 +4,8 @@ import { derivePaperclipPrReview, evaluatePrReviewCompletionEvidence, mergeCoalescedContextSnapshot, + prReviewAlreadyReviewedVetoCue, + prReviewOutputHasAlreadyReviewedSkip, summarizeHeartbeatRunContextSnapshot, summarizeHeartbeatRunListResultJson, } from "../services/heartbeat.js"; @@ -726,6 +728,689 @@ describe("evaluatePrReviewCompletionEvidence", () => { ).toEqual({ status: "already_reviewed" }); }); + // BLO-31374: the same idempotent exit as the reviewer actually writes it — + // markdown-formatted, and with the sha either after `for` or directly after + // `at`. Both texts are verbatim openings of real Ally runs on 2026-09-02 that + // exited cleanly and were still classified `pr_review_output_missing`, + // flipping Ally to `error`. + it.each([ + { + label: "run b7a984bf — sha directly after `at`, bold + backticks", + summary: + "**Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`** — no action taken.\n\n" + + "The wake was a `transient_failure_retry` carrying a stale head (`0936fba6…`). Against the live PR state:\n\n" + + "- **Live head:** `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` (the wake-time SHA is superseded).\n" + + "- **Existing review:** `5096237327` — `allyblockcast[bot]` (Bot), state `COMMENTED`, submitted 2026-09-02T23:28:20Z, " + + "body starting `## Ally — Consolidated PR Review` with exactly one `Reviewed head:` line attesting `8b237675…`.\n\n" + + "Posting again would be a duplicate verdict on the same head, which the one-review-per-(PR, head) contract prohibits. Exiting cleanly.", + }, + { + label: "run 3ace1eef — timestamp + `for` + backticked sha", + summary: + "Exiting without posting — the idempotency check proves this head was already reviewed.\n\n" + + "## Wake disposition: already reviewed\n\n" + + "**`Blockcast/penstock-vault-node#554`** — the wake carried head `8d47ae36`, which has been superseded. " + + "Live head is **`90193c30abb9a75ac17e167b9aea8ca83cebc2cb`**, and the PR is **merged**.\n\n" + + "**Already reviewed at 2026-09-02T20:41:53Z for `90193c30abb9a75ac17e167b9aea8ca83cebc2cb`** (review `5094874877`).\n\n" + + "No review posted, no PR state touched.", + }, + { + label: "sha after a `head` noun", + summary: "Already reviewed at head 90193c30abb9a75ac17e167b9aea8ca83cebc2cb; skipping.", + }, + ])("BLO-31374: accepts a markdown-formatted already-reviewed exit ($label)", ({ summary }) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ + status: "already_reviewed", + }); + }); + + // Masking guard: an "already reviewed" claim that cites no sha is not an + // idempotency exit — nothing ties it to a head — and stays `missing`. + it("BLO-31374: rejects an already-reviewed claim that cites no sha", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "Already reviewed at 2026-09-02T20:41:53Z; nothing further to do.", + }), + ).toMatchObject({ status: "missing", errorCode: "pr_review_output_missing" }); + }); + + // Masking guard: a negated clause describes the opposite situation — and the + // negation must survive the same markdown the clause tolerates (Ally review + // of #1613: a bare `\s+` prefix let `**not**` through). + it.each([ + { label: "plain not", summary: "This head was not already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; the draft review was never posted." }, + { label: "bold not", summary: "This head was **not** already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; the draft review was never posted." }, + { label: "italic not", summary: "It was *not* already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`." }, + { label: "not yet been", summary: "Has not yet been already reviewed at 8b237675b19fa5ae061821fd3b1d87cd8cd1836f." }, + ])("BLO-31374: rejects a negated already-reviewed clause ($label)", ({ summary }) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Masking guard: hedged or prior-head narration from a run that did NOT post + // (Ally review of #1613). The old `for`-anchored regex rejected all three. + it.each([ + { label: "could not confirm whether", summary: "I could not confirm whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; the API call failed." }, + { label: "unclear if", summary: "Unclear if already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`. Aborting before the post step." }, + { label: "prior head, did not post", summary: "The prior head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`, but the branch moved and I did not post a review for the new head." }, + { label: "unclear that + copula", summary: "It is unclear that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`." }, + ])("BLO-31374: rejects hedged or prior-head already-reviewed narration ($label)", ({ summary }) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Not vetoed: stating that no second review was posted is the defining + // property of this exit (second Ally pass on #1613 — a posted-negation veto + // sent all five of these to `missing`). + it.each([ + "**Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`** — I did not post a second review.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; I didn't post again.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`. Did not post a duplicate verdict.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — could not post a duplicate; contract forbids it.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; unable to post a second verdict on the same head.", + ])("BLO-31374: a correct skip that says it did not post again still classifies (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ status: "already_reviewed" }); + }); + + // Precedence guard: a hedge or bare `if`/`whether` in an earlier clause does + // not veto a later unhedged clause — whatever joins the two clauses. + it.each([ + "I checked whether a review exists. Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — no action taken.", + "Checked whether a prior review exists: already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Checked whether a prior review exists, already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Checked whether a prior review exists — already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Checked whether a prior review exists; already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Skipping the post step if a review exists; already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Determining if this is a duplicate: already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: an earlier clause's hedge or bare if/whether does not veto the clause (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ status: "already_reviewed" }); + }); + + // Pins the two halves of the hedge veto independently. (1) A bare + // `whether`/`if` in the SAME clause that does not govern the review clause + // must not veto — only a governing hedge ("unclear whether", "could not + // confirm whether") does. (2) A governing hedge about something else in the + // PREVIOUS clause, joined by ; : , or —, is out of scope for the review + // clause. + it.each([ + "Whether the wake was stale is moot because the head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Regardless of if the wake head moved, this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Unclear whether CI is green; already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — no action taken.", + "I could not confirm whether CI passed: already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Not sure if the lockstep check ran, already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: only a hedge governing the review clause in its own clause vetoes (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ status: "already_reviewed" }); + }); + + // Stale/prior-head narration AFTER the clause is how a correct skip explains + // the wake (third Ally pass); only a prior-head subject BEFORE the clause + // vetoes. + it.each([ + "**Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`** — the wake carried a stale head, superseded by this one.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`, the wake head has moved since the original wake.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — the branch moved after the wake, so the wake SHA is not the live one.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; the prior head in the payload is superseded.", + "Already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` (the earlier head `0936fba6` is stale) — no action taken.", + ])("BLO-31374: stale-head narration after the clause does not veto (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ status: "already_reviewed" }); + }); + + // Markdown closing before the sha: the clause interior tolerates the same + // markdown class as its tail. + it.each([ + "**Already reviewed** at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — no action taken.", + "*Already reviewed* at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "`Already reviewed` at 8b237675b19fa5ae061821fd3b1d87cd8cd1836f.", + ])("BLO-31374: markdown that closes before the sha still matches (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ status: "already_reviewed" }); + }); + + it("BLO-31374: a glued `alreadyreviewed` is not the clause", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "alreadyreviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + }), + ).toMatchObject({ status: "missing", errorCode: "pr_review_output_missing" }); + }); + + // Multi-word hedges still govern the clause. + it.each([ + "I could not fully confirm whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; aborting.", + "I was not able to confirm whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: a multi-word governing hedge still vetoes (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Masking guard (fourth Ally pass): a negation governing the clause from the + // subject position, not adjacent to `already`. All four were `false` on + // master and must stay `missing`. + it.each([ + "There is no evidence this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`, so I posted a fresh verdict.", + "I do not believe this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I cannot see that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Found no review; nothing indicates this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: rejects a non-adjacent negation governing the clause (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // A correct skip explains WHY it did not post, often with a negation word in + // the same clause and before the review clause (fifth Ally pass). Only an + // epistemic negation vetoes; these all classify. + it.each([ + "Exiting without posting since this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I did not post a duplicate because this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "There is no need to post again because this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "No action taken because this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Nothing to do here because the head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I don't need to post since the head was already reviewed at 2026-09-02T20:41:53Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f.", + "The contract does not permit a second verdict and this head was already reviewed at 2026-09-02T20:41:53Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f.", + "I cannot post a second verdict because this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "No second verdict is needed as this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Skipping — no duplicate verdict is permitted and this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: a non-epistemic negation explaining the skip does not veto (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ status: "already_reviewed" }); + }); + + it("BLO-31374: 'failed to confirm whether' is a governing hedge", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "I failed to confirm whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; aborting.", + }), + ).toMatchObject({ status: "missing", errorCode: "pr_review_output_missing" }); + }); + + // Masking guard (sixth Ally pass): a `that`-complement of an establishing + // verb that the run could NOT complete. The hedge only sees `if|whether`. + it.each([ + "I could not verify that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I could not establish that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I could not determine that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I could not find that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I have not verified that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Unable to establish that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I could not check that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`; the API failed.", + "I couldn't verify that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I failed to establish that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: rejects a failed establishing verb with a that-complement (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Seventh Ally pass, over-veto direction: the establishing verbs are + // transitive over arbitrary objects, so a correct skip that negates finding + // or checking a DIFFERENT thing must still classify as a skip. Master + // accepted every one of these in the plain shape. + it.each([ + "I did not find a newer head so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "No newer review was found so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "I could not find any reason to re-review so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "I did not check the comments API but already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "I could not see a newer head so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "Cannot tell you more; nothing else found so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "I did not find a newer head so **already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`** — no action taken.", + ])("BLO-31374: a negated establishing verb with an unrelated object is still a skip (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ + status: "already_reviewed", + }); + }); + + // …while the same verb bound to the clause by a complementizer still vetoes. + it("BLO-31374: the complementizer is what binds a negated establishing verb to the clause", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "I did not find that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + }), + ).toMatchObject({ status: "missing", errorCode: "pr_review_output_missing" }); + }); + + // Seventh Ally pass, masking direction: hedges outside the original four + // stems, `that` after a hedge stem, and assumptions with no negation and no + // complementizer at all. + it.each([ + "Unknown whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "It remains unverified whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I have no idea whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "It is ambiguous whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "It is not clear that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I doubt this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "Possibly already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`, but I could not check.", + "Assuming this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` (not confirmed).", + ])("BLO-31374: rejects a hedge or assumption governing the clause (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Eighth Ally pass, over-veto direction: hedging about the WAKE is the most + // natural thing this exit says, and the assumption stem must not veto it. + // Master accepted all six. + it.each([ + "The wake was probably a duplicate dispatch so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "This is presumably a retry wake so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "The retry was apparently spurious so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "The PR may have been updated since but already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "Possibly a duplicate wake and already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "The wake payload was probably stale so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + ])("BLO-31374: an assumption about the wake is still a skip (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ + status: "already_reviewed", + }); + }); + + // Eighth Ally pass, masking direction: the complementizer elided, the verb + // bound to the clause by the copula at the clause edge. + it.each([ + "I cannot say this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I cannot state this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I could not confirm this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I could not establish this head had been already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: rejects an elided complementizer bound by a copula (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Ninth Ally pass, over-veto direction: an epistemic NOUN whose negated + // object is not the review claim. Master accepted all of these. + it.each([ + "No evidence of a force-push, so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "No indication the branch advanced, so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "There is no record of a newer wake, and already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "I do not believe the payload was fresh, but already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "Nothing suggests a retry storm; already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + "I did not check whether a newer head exists so already reviewed at 2026-09-02T23:31:00Z for 8b237675b19fa5ae061821fd3b1d87cd8cd1836f", + ])("BLO-31374: a negated NON-review object is still a skip (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toEqual({ + status: "already_reviewed", + }); + }); + + // …and the same nouns bound to the clause still veto. + it.each([ + "No evidence that this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "No indication this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I do not believe this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: an epistemic noun bound to the clause still vetoes (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // Ninth pass, masking direction: a hedge outside the stem vocabulary that + // still questions THIS clause through `whether`. + it.each([ + "It remains an open question whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "The evidence is inconclusive as to whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + "I would have to guess whether this head was already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + ])("BLO-31374: any question whose complement is the clause vetoes (%#)", (summary) => { + expect(evaluatePrReviewCompletionEvidence(reviewerContext, { summary })).toMatchObject({ + status: "missing", + errorCode: "pr_review_output_missing", + }); + }); + + // An assumption in the PREVIOUS clause does not reach the review clause. + it("BLO-31374: an assumption in an earlier clause does not veto the clause", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "Presumably the earlier run posted; already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — no action taken.", + }), + ).toEqual({ status: "already_reviewed" }); + }); + + // A negation in the PREVIOUS clause does not reach the review clause. + it("BLO-31374: a negation in an earlier clause does not veto the clause", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "No changes were requested on the prior head; already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f` — no action taken.", + }), + ).toEqual({ status: "already_reviewed" }); + }); + + // Direct table over the predicate: pins each veto boundary in one line. + describe("BLO-31374: prReviewOutputHasAlreadyReviewedSkip boundaries", () => { + const sha = "8b237675b19fa5ae061821fd3b1d87cd8cd1836f"; + it.each([ + [`Already reviewed at \`${sha}\` — no action taken.`, true], + [`already reviewed at 2026-05-26T04:38:27Z for ${sha}`, true], + [`**Already reviewed** at \`${sha}\`.`, true], + [`Already reviewed at head ${sha}; skipping.`, true], + [`Already reviewed at ${sha} — the wake carried a stale head, superseded by this one.`, true], + [`Checked whether a prior review exists: already reviewed at \`${sha}\`.`, true], + [`Unclear whether CI is green; already reviewed at \`${sha}\`.`, true], + [`Already reviewed at \`${sha}\`; I did not post again.`, true], + [`Already reviewed at 2026-09-02T20:41:53Z; nothing further to do.`, false], + [`This head was **not** already reviewed at \`${sha}\`.`, false], + [`There is no evidence this head was already reviewed at \`${sha}\`.`, false], + [`I cannot see that this head was already reviewed at \`${sha}\`.`, false], + [`Exiting without posting since this head was already reviewed at \`${sha}\`.`, true], + [`No action taken because this head was already reviewed at \`${sha}\`.`, true], + [`I failed to confirm whether this head was already reviewed at \`${sha}\`.`, false], + [`I could not verify that this head was already reviewed at \`${sha}\`.`, false], + [`I did not find a newer head so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I did not find that this head was already reviewed at \`${sha}\`.`, false], + [`Possibly already reviewed at \`${sha}\`, but I could not check.`, false], + [`The wake was probably a duplicate dispatch so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I cannot say this head was already reviewed at \`${sha}\`.`, false], + [`No evidence of a force-push, so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence that this head was already reviewed at \`${sha}\`.`, false], + [`It remains an open question whether this head was already reviewed at \`${sha}\`.`, false], + [`Unable to establish that this head was already reviewed at \`${sha}\`.`, false], + [`Unclear if already reviewed at \`${sha}\`. Aborting.`, false], + [`It is unclear that this head was already reviewed at \`${sha}\`.`, false], + [`Unclear that a rerun helps so already reviewed at \`${sha}\`.`, true], + [`I could not fully confirm whether this head was already reviewed at \`${sha}\`.`, false], + [`The prior head was already reviewed at \`${sha}\`, but the branch moved.`, false], + [`Nothing indicates the branch moved so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Checked that no stale head is involved so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`The old head and the current head are the same so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No force-push, so the branch moved nowhere and already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`The branch has moved so the earlier head was already reviewed at \`${sha}\`.`, false], + [`The stale head was already reviewed at \`${sha}\`.`, false], + [`The reviews API 500'd; I am guessing this head was already reviewed at \`${sha}\`.`, false], + [`I guess this head was already reviewed at \`${sha}\`.`, false], + [`My best guess is that this head was already reviewed at \`${sha}\`.`, false], + [`The wake was a guess at the head so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`My guess about the retry cause is irrelevant so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`There is no doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I have no doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Without doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Beyond doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`It is not in doubt that this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I doubt this head was already reviewed at \`${sha}\`.`, false], + [`There is some doubt this head was already reviewed at \`${sha}\`.`, false], + [`It is doubtful this head was already reviewed at \`${sha}\`.`, false], + [`alreadyreviewed at \`${sha}\`.`, false], + [`Already reviewed at${sha}.`, false], + [`There is no real doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`There is no serious doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Beyond reasonable doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Without any doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I have no genuine doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`There is not the slightest doubt this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer commits were found so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer head was found so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing new was seen so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No later commit was found so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No other review was found so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer head was found but this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer head was found because this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`It appears this head was already reviewed at \`${sha}\`.`, false], + [`It seems likely this head was already reviewed at \`${sha}\`.`, false], + [`I believe this head was already reviewed at \`${sha}\`.`, false], + [`I think this head was already reviewed at \`${sha}\`.`, false], + [`I suspect this head was already reviewed at \`${sha}\`.`, false], + [`Perhaps this head was already reviewed at \`${sha}\`.`, false], + [`Maybe this head was already reviewed at \`${sha}\`.`, false], + [`It looks like this head was already reviewed at \`${sha}\`.`, false], + [`It is plausible this head was already reviewed at \`${sha}\`.`, false], + [`There is no confirmation this head was already reviewed at \`${sha}\`.`, false], + [`I cannot rule out that this head was already reviewed at \`${sha}\`.`, false], + [`My assumption is that this head was already reviewed at \`${sha}\`.`, false], + [`My assumption about the wake was wrong so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`It appears no new commits landed so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`This looks like a duplicate wake so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer commits were found yet this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found however this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found though this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found although this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found still this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found nonetheless this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found nevertheless this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found whereas this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found while this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found then this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found consequently this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found accordingly this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found, yet this head was already reviewed at \`${sha}\`.`, true], + [`Already reviewed at 20260902T204153Z`, false], + [`Already reviewed at 1234567`, false], + [`Already reviewed at 2026-09-02T20:41:53Z`, false], + [`Already reviewed at 2026-09-02T23:31:00Z for 1234567`, true], + [`Already reviewed at head 1234567`, true], + [`Already reviewed at commit 1234567`, true], + [`Already reviewed at 8B237675B19FA5AE061821FD3B1D87CD8CD1836F`, false], + [`Already reviewed at \`${sha}\`.`, true], + [`This head never was already reviewed at \`${sha}\`.`, false], + [`This head not yet was already reviewed at \`${sha}\`.`, false], + [`This head was never already reviewed at \`${sha}\`.`, false], + [`Already reviewed at head 8B237675B19FA5AE061821FD3B1D87CD8CD1836F`, false], + [`Already reviewed at 2026-09-02T23:31:00Z for 8B237675B19FA5AE061821FD3B1D87CD8CD1836F`, false], + [`Already reviewed at 8b237675B19FA5AE061821FD3B1D87CD8CD1836F`, false], + [`No confirmation yet that this head was already reviewed at \`${sha}\`.`, false], + [`No evidence yet that this head was already reviewed at \`${sha}\`.`, false], + [`There is no record yet that this head was already reviewed at \`${sha}\`.`, false], + [`No indication yet that this head was already reviewed at \`${sha}\`.`, false], + [`I have no proof yet that this head was already reviewed at \`${sha}\`.`, false], + [`No sign yet that this head was already reviewed at \`${sha}\`.`, false], + [`Cannot confirm yet that this head was already reviewed at \`${sha}\`.`, false], + [`No verification yet that this head was already reviewed at \`${sha}\`.`, false], + [`No confirmation still that this head was already reviewed at \`${sha}\`.`, false], + [`No evidence however that this head was already reviewed at \`${sha}\`.`, false], + [`No record though that this head was already reviewed at \`${sha}\`.`, false], + [`No newer commits were found yet this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found however this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found though this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found still this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found nonetheless this head was already reviewed at \`${sha}\`.`, true], + [`No newer commits were found whereas this head was already reviewed at \`${sha}\`.`, true], + [`No evidence while checking that this head was already reviewed at \`${sha}\`.`, false], + [`No confirmation from the reviews API yet that this head was already reviewed at \`${sha}\`.`, false], + [`I could not find any record in the last hour that this head was already reviewed at \`${sha}\`.`, false], + [`No newer commits were found in the last hour so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`It appeared this head was already reviewed at \`${sha}\`.`, false], + [`It looked like this head was already reviewed at \`${sha}\`.`, false], + [`I am thinking this head was already reviewed at \`${sha}\`.`, false], + [`It is probable this head was already reviewed at \`${sha}\`.`, false], + [`It is possible this head was already reviewed at \`${sha}\`.`, false], + [`It is conceivable this head was already reviewed at \`${sha}\`.`, false], + [`It is presumable this head was already reviewed at \`${sha}\`.`, false], + [`It is apparent this head was already reviewed at \`${sha}\`.`, false], + [`My presumption is that this head was already reviewed at \`${sha}\`.`, false], + [`My belief is that this head was already reviewed at \`${sha}\`.`, false], + [`My impression is that this head was already reviewed at \`${sha}\`.`, false], + [`My understanding is that this head was already reviewed at \`${sha}\`.`, false], + [`It appeared no new commits landed so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`My understanding of the wake was wrong so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`My impression about the retry cause is irrelevant so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`It is possible to re-run the gate so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Already reviewed at 1234567 (sha \`${sha}\`)`, true], + [`Already reviewed at 20260902T204153Z commit ${sha}`, true], + [`Already reviewed at 1234567 and the deadbeef branch`, false], + [`Already reviewed at 1234567 (see deadbeef)`, false], + [`Already reviewed at 1234567. The commit ${sha} is unrelated.`, false], + [`No evidence ${"word ".repeat(23)}was already reviewed at \`${sha}\`.`, true], + [`No evidence ${"word ".repeat(6)}was already reviewed at \`${sha}\`.`, false], + [`Already reviewed at head: ${sha}`, true], + [`Already reviewed at the head ${sha}`, true], + [`Already reviewed at current head ${sha}`, true], + [`Already reviewed at head sha ${sha}`, true], + [`Already reviewed at commit sha ${sha}`, true], + [`Already reviewed at head=${sha}`, true], + [`Already reviewed at sha 1234567`, false], + [`Already reviewed at head sha 1234567`, true], + [`I haven't confirmed that this head was already reviewed at \`${sha}\`.`, false], + [`I hadn't verified that this head was already reviewed at \`${sha}\`.`, false], + [`The run didn't establish that this head was already reviewed at \`${sha}\`.`, false], + [`The API hasn't confirmed that this head was already reviewed at \`${sha}\`.`, false], + [`The checks weren't showing that this head was already reviewed at \`${sha}\`.`, false], + [`I am failing to confirm that this head was already reviewed at \`${sha}\`.`, false], + [`There was a failure to confirm that this head was already reviewed at \`${sha}\`.`, false], + [`Nothing indicated this head was already reviewed at \`${sha}\`.`, false], + [`There is no suggestion that this head was already reviewed at \`${sha}\`.`, false], + [`There are no records that this head was already reviewed at \`${sha}\`.`, false], + [`There are no traces that this head was already reviewed at \`${sha}\`.`, false], + [`I never saw that this head was already reviewed at \`${sha}\`.`, false], + [`I am unaware that this head was already reviewed at \`${sha}\`.`, false], + [`No statement that this head was already reviewed at \`${sha}\`.`, false], + [`Nothing was demonstrating that this head was already reviewed at \`${sha}\`.`, false], + [`The run never told me that this head was already reviewed at \`${sha}\`.`, false], + [`I could not rule out that this head was already reviewed at \`${sha}\`.`, false], + [`Nothing indicated a newer commit so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No records of a force-push so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I never saw a newer head so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I am unaware of any newer commits so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer commit was found for the head that was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing found since the commit that was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`I could not find any commit newer than the one that was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`The head moved to a commit which was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence that this head was already reviewed at \`${sha}\`.`, false], + [`No evidence while checking that this head was already reviewed at \`${sha}\`.`, false], + [`I cannot confirm \`8b23767\` was already reviewed at \`${sha}\`.`, false], + [`I could not verify this PR's head was already reviewed at \`${sha}\`.`, false], + [`I could not verify (after two retries) this head was already reviewed at \`${sha}\`.`, false], + [`There is no evidence - none - this head was already reviewed at \`${sha}\`.`, false], + [`I cannot confirm "this" head was already reviewed at \`${sha}\`.`, false], + [`I cannot confirm the wake. This head was already reviewed at \`${sha}\`.`, true], + [`No evidence of a force-push; this head was already reviewed at \`${sha}\`.`, true], + [`I remain unconvinced that this head was already reviewed at \`${sha}\`.`, false], + [`Unaware whether this head was already reviewed at \`${sha}\`.`, false], + [`I am unaware of any newer commits so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Unconvinced the wake was legitimate so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing showed that this head was already reviewed at \`${sha}\`.`, false], + [`Nothing showed a newer commit so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence yet this head was already reviewed at \`${sha}\`.`, false], + [`No evidence still this head was already reviewed at \`${sha}\`.`, false], + [`No evidence however this head was already reviewed at \`${sha}\`.`, false], + [`No indication yet this head was already reviewed at \`${sha}\`.`, false], + [`No indication still this head was already reviewed at \`${sha}\`.`, false], + [`No indication however this head was already reviewed at \`${sha}\`.`, false], + [`No confirmation yet this head was already reviewed at \`${sha}\`.`, false], + [`No confirmation still this head was already reviewed at \`${sha}\`.`, false], + [`No confirmation however this head was already reviewed at \`${sha}\`.`, false], + [`I cannot say yet this head was already reviewed at \`${sha}\`.`, false], + [`I cannot say still this head was already reviewed at \`${sha}\`.`, false], + [`I cannot say however this head was already reviewed at \`${sha}\`.`, false], + [`There is no record yet this head was already reviewed at \`${sha}\`.`, false], + [`There is no record still this head was already reviewed at \`${sha}\`.`, false], + [`There is no record however this head was already reviewed at \`${sha}\`.`, false], + [`Nothing suggests yet this head was already reviewed at \`${sha}\`.`, false], + [`Nothing suggests still this head was already reviewed at \`${sha}\`.`, false], + [`Nothing suggests however this head was already reviewed at \`${sha}\`.`, false], + [`No newer commits were found yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing new was seen still this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence of a force-push however this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer commits were found yet that head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer commits were found so that commit was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing new was seen still that head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence yet that this head was already reviewed at \`${sha}\`.`, false], + [`No evidence still that this head was already reviewed at \`${sha}\`.`, false], + [`I assume this head was already reviewed at \`${sha}\`.`, false], + [`I assumed this head was already reviewed at \`${sha}\`.`, false], + [`The run assumes this head was already reviewed at \`${sha}\`.`, false], + [`My assumptions are that this head was already reviewed at \`${sha}\`.`, false], + [`I presume this head was already reviewed at \`${sha}\`.`, false], + [`Possibly, this head was already reviewed at \`${sha}\`.`, false], + [`Probably, this head was already reviewed at \`${sha}\`.`, false], + [`Perhaps, this head was already reviewed at \`${sha}\`.`, false], + [`I checked the wake. Possibly, a retry so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`The wake was possibly a duplicate, so already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Not possibly, this head was already reviewed at \`${sha}\`.`, true], + [`No clear evidence yet this head was already reviewed at \`${sha}\`.`, false], + [`No direct evidence yet this head was already reviewed at \`${sha}\`.`, false], + [`No strong indication yet this head was already reviewed at \`${sha}\`.`, false], + [`No independent confirmation yet this head was already reviewed at \`${sha}\`.`, false], + [`There is no real evidence yet this head was already reviewed at \`${sha}\`.`, false], + [`No further indication still this head was already reviewed at \`${sha}\`.`, false], + [`No newer commits were found yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer revisions were seen yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing new has been found still this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer head is known yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No clear evidence because this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing else found so this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing new seen therefore this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No confirmation hence this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No indication thus this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing else found consequently this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No confirmation accordingly this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`Nothing else found therefore this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No evidence then this head was already reviewed at \`${sha}\`.`, false], + [`No newer commits appear yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No newer revisions indicate yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No other branches suggest yet this head was already reviewed at 2026-09-02T23:31:00Z for ${sha}`, true], + [`No clear evidence yet this head was already reviewed at \`${sha}\`.`, false], + [`No strong indication still this head was already reviewed at \`${sha}\`.`, false], + [`Already reviewed at head${sha}.`, false], + ])("%s → %s", (text, want) => { + expect(prReviewOutputHasAlreadyReviewedSkip(text)).toBe(want); + }); + }); + + // Attribution: which cue vetoes. Eleven review passes have each needed a + // by-hand bisection to answer this; the table below pins one representative + // clause per cue so the next regression report can name the cue directly. + describe("BLO-31374: veto attribution names the governing cue", () => { + it.each([ + ["It is unclear that this head was ", "hedge"], + ["Possibly ", "assumption"], + ["I am guessing this head was ", "assumption"], + ["I doubt this head was ", "assumption"], + ["It remains an open question whether this head was ", "questioned"], + ["Unclear if ", "questioned"], + ["The prior head was ", "priorHead"], + ["No evidence that this head was ", "negation"], + ["I cannot say this head was ", "negation"], + ["I am unaware that this head was ", "fusedNegation"], + ["No evidence yet this head was ", "negatedHeadConnective"], + ["No evidence that this head was ", "negation"], + ["I am not aware that this head was ", "negation"], + // Correct skips: no cue governs the clause, so nothing vetoes. + ["No evidence of a force-push, so ", null], + ["I did not check whether a newer head exists so ", null], + ["No newer commits were found so this head was ", null], + ["Beyond reasonable doubt this head was ", null], + ["The wake was probably a duplicate dispatch so ", null], + ])("%s -> %s", (before, cue) => { + expect(prReviewAlreadyReviewedVetoCue(before)).toBe(cue); + }); + }); + + // A hedge about something OTHER than the review does not mask the skip: the + // `that`-complement closes before the clause, so no copula reaches it. Drop + // the copula bind from the hedge cue and this row flips to `missing` — the + // false-`missing` regression this PR exists to eliminate. + it("BLO-31374: a hedge whose complement closes before the clause still skips", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "Unclear that a rerun helps so already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + }), + ).toMatchObject({ status: "already_reviewed" }); + }); + + // `notalready` is not a negation and not the clause: no word boundary before + // `already`, so the shape does not match at all and the run stays `missing`. + it("BLO-31374: a glued `notalready` is neither a negation nor the clause", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "This head was notalready reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + }), + ).toMatchObject({ status: "missing", errorCode: "pr_review_output_missing" }); + }); + + it("BLO-31374: rejects a plural negation", () => { + expect( + evaluatePrReviewCompletionEvidence(reviewerContext, { + summary: "These heads weren't already reviewed at `8b237675b19fa5ae061821fd3b1d87cd8cd1836f`.", + }), + ).toMatchObject({ status: "missing", errorCode: "pr_review_output_missing" }); + }); + it("accepts archived Network-Management-Portal skips", () => { expect( evaluatePrReviewCompletionEvidence( diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index 5b4e64ca352..943f0fcdc90 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -9466,6 +9466,412 @@ function prReviewOutputHasSelfReviewSkip( return handlePattern.test(text); } +// BLO-31374: the idempotent "already reviewed" exit. The reviewer found an Ally +// review whose body attests the LIVE head and exited without posting, per the +// one-review-per-(PR, head) contract. Routine on `transient_failure_retry` wakes, +// which carry the head captured at the original wake and are often stale by the +// time the retry runs. Real shapes, all of which must classify `already_reviewed`: +// "already reviewed at 2026-05-26T04:38:27Z for 86fd374dc3b4…" (plain) +// "**Already reviewed at 2026-09-02T20:41:53Z for `90193c30abb9…`**" (run 3ace1eef) +// "**Already reviewed at `8b237675b19f…`** — no action taken." (run b7a984bf) +// The previous inline regex accepted only the plain shape: a backtick between +// `for` and the sha, or the sha following `at` directly, fell through to +// `pr_review_output_missing` and flipped Ally to `error`. The cited sha is the +// live head, which the wake context cannot know (the wake head is exactly what +// went stale), so the clause is anchored to a 7–40 hex sha rather than to the +// wake head — a sha-less "already reviewed" claim is NOT accepted. Five cues +// keep the widened shape from masking a run that did NOT post (per Ally reviews +// of #1613) — an adjacent negation, plus the five GOVERNING_CUE_STEMS tested +// against the clause scope (hedge, assumption, questioned, priorHead, +// negation), so six in total, all sharing one +// clause-binding rule: a negated clause ("was **not** already reviewed at …" — the +// negation prefix tolerates the same markdown the clause does, so bold/italic +// cannot slip past it), a hedge that governs the clause in the same sentence +// ("could not confirm whether … already reviewed at …", "Unclear if already +// reviewed at …"), and a prior/stale-head cue governing the clause ("the prior +// head was already reviewed at …"). NOT vetoed: "did not post" — not posting is +// the defining property of this exit, so the shared +// prReviewOutputHasPostedReviewNegation cues would invert the branch (second +// Ally pass: five plausible phrasings of a correct skip fell to `missing`). +// Also NOT vetoed: stale/prior-head narration AFTER the clause ("… — the wake +// carried a stale head, superseded by this one") — that is how a correct skip +// explains the wake (third Ally pass), so every clause-scoped cue looks only at +// the text BEFORE the clause, and that scope ends at . : ; , — – or a newline, so a +// clause joined by a colon or dash does not inherit a hedge from the previous +// clause. The same before-scope also carries an EPISTEMIC negation cue for the +// non-adjacent form ("no evidence this head was already reviewed at …", +// "I cannot see that this head was already reviewed at …" — fourth Ally pass: +// the adjacent form alone let these through as `already_reviewed`, the masking +// direction). It is anchored to an epistemic head (evidence / indication / +// believe / see / confirm …) exactly as `hedge` is anchored, because a bare +// negation word list vetoes the house phrasing of a CORRECT skip — "Exiting +// without posting since this head was already reviewed at …", "No action +// taken because …" (fifth Ally pass: 10 of 10 such phrasings fell to +// `missing`). The scope is capped at 120 characters before the clause; a +// comma-free run-on longer than that escapes the vetoes, which no real +// reviewer sentence has approached (commas are themselves boundaries), and an +// uncapped `[^…]*$` scan is quadratic on long outputs. +// THE discriminator, shared by every governing cue in GOVERNING_CUES below: a +// head only vetoes the already-reviewed clause if it REACHES it. Either it sits +// at the clause edge, or it gets there through the copula ("no evidence that +// this head WAS already reviewed at …", "I cannot say this head WAS already +// reviewed at …"). A correct skip has so / but / and / because in that position +// instead ("no evidence of a force-push, so already reviewed at …", "I did not +// find a newer head so already reviewed at …"). +// +// A complementizer alone is NOT enough and is deliberately not part of the +// rule: `that`/`whether` binds the head to SOME clause, not necessarily this +// one — "I did not check whether a newer head exists so already reviewed at …" +// is a correct skip whose whether-clause is about the head, not the review. An +// earlier draft accepted a bare complementizer and vetoed exactly that +// sentence. The copula is the anchor; a complementizer may precede it and is +// simply absorbed as one of the intervening words. +// The filler EXCLUDES the connectives, so the code holds the invariant the +// comment above documents. `\w+` matches `so`/`but`/`and`/`because`, so a bare +// filler swallowed the very words that mark a correct skip and let a negation +// governing a DIFFERENT noun phrase reach the clause across the connective — +// "No newer commits were found so this head was already reviewed at …" +// (eleventh pass: 5 of 5 regressed against master). +// Two kinds of connective, which the copula filler treats alike but the +// elided-complement cue must NOT: a CONSEQUENCE connective introduces a new +// clause whose subject is what follows ("nothing else found SO this head was"), +// so a negated head before it governs the PREVIOUS clause and never this one; +// an ADVERSATIVE can sit inside a single clause with the complementizer +// dropped ("no clear evidence YET this head was"). Measured (pass 17): treating +// them alike vetoed 6 correct skips of the form "no evidence so this head was". +const CONSEQUENCE_CONNECTIVES = "so|because|since|therefore|thus|hence|consequently|accordingly|and|but"; +const ADVERSATIVE_CONNECTIVES = + "yet|however|though|although|still|nonetheless|nevertheless|whereas|while|then"; +// `consequently`/`accordingly` are exact synonyms of therefore/hence and have no +// adversative reading, so they belong with the consequences (pass 18: an +// identical frame diverged purely on which synonym the run wrote). `then` stays +// here on purpose: it has a real filler reading — "no evidence THEN that …" = +// "no evidence at that time" — so its veto is defensible. +const CONNECTIVES = `${CONSEQUENCE_CONNECTIVES}|${ADVERSATIVE_CONNECTIVES}`; +// The connectives are DUAL-ROLE words, so the exclusion is conditioned on the +// role rather than applied to the word. In the CONNECTIVE role the word is +// followed by the subject ("...were found yet this head was already reviewed"), +// and the filler must not cross it. In the FILLER role it is followed by a +// complementizer ("no confirmation yet THAT this head was already reviewed"), +// where it is ordinary English for "I could not establish this" and the +// negation must still reach the clause. The complementizer need not be +// adjacent — "no evidence while CHECKING that this head was …" puts a +// participle in between — so the role test allows it up to two words out. Excluding them unconditionally traded +// 12 filler-sense vetoes for 12 connective-sense acceptances 1:1 and lost the +// side a reviewer actually writes (fifteenth pass); the nested lookahead is +// strictly better than either. +// The filler bound is DERIVED from the clause cap, and is deliberately +// NON-BINDING. There is exactly one operative limit on how far a cue reaches — +// CLAUSE_SCOPE_CHARS — and this bound exists only to keep the quantifier +// bounded for ReDoS. Sizing it at CLAUSE_SCOPE_CHARS / 5 (a conservative 5 +// characters per word including its space) makes it wider than the character +// window can ever fill: a filler of that many words consumes the whole window +// and crowds out the cue word itself, so the character cap always binds first. +// Measured: "No evidence" + 23 filler words + "was" does NOT veto, because the +// negation falls outside the 120-character slice. +// +// The previous literal 4 was a SECOND, tighter cap with no derivation, and it +// silently truncated reach before the connective logic ran: "no evidence while +// checking that this head was already reviewed at …" is five filler words, so +// the negation never reached the clause and the claim was accepted (fifteenth +// pass, pre-existing at every prior head). +const CLAUSE_SCOPE_CHARS = 120; +const COPULA_FILLER_WORDS = Math.floor(CLAUSE_SCOPE_CHARS / 5); +// The filler token is anything that is NOT a clause boundary, rather than a +// word character. The excluded set is EXACTLY clauseBefore's boundary set plus +// whitespace, so the filler can cross a backtick, a possessive, a parenthesis +// or a hyphen but can never cross a boundary — one definition of "clause", +// used in both places. With `\\w+` the cue was blocked by its own citation: +// "I cannot confirm `` was already reviewed at …" leaked (masking). +// …and a complementizer immediately followed by an anchor NOUN is a +// demonstrative, not a complementizer ("yet THAT HEAD was" vs "yet that THIS +// head was"), so it must not flip the connective into its filler role and let +// the negation cross (Ally pass 16 suggestion: 20/20 connectives false-vetoed). +const COPULA_FILLER = `(?:(?!(?:${CONNECTIVES})\\b(?!\\s+(?:[^\\s.\\n:;,\u2014\u2013]+\\s+){0,2}(?:that|whether)\\b(?!\\s+(?:head|commit|sha|branch|pr)\\b)))[^\\s.\\n:;,\u2014\u2013]+\\s+){0,${COPULA_FILLER_WORDS}}`; +// A relative pronoun abutting the copula is NOT a complementizer, and the +// distinction is syntactic rather than lexical: a complementizer is always +// followed by a SUBJECT before the copula ("confirm that this head was"), so +// `that` sitting directly against the copula can only head a relative clause +// modifying the preceding noun phrase. Without the lookbehind the cue reached +// across it and refused correct skips like "no newer commit was found for the +// head that was already reviewed at …" (false-missing direction). +const COPULA_EDGE = `${COPULA_FILLER}(? = [ + // A hedge governing the clause through `that` — "It is not clear THAT this + // head was already reviewed at …". `that` also introduces assertions ("I + // confirmed that this head was already reviewed at …"), so unlike + // `whether`/`if` it needs a hedging stem in front of it. Every `whether`/`if` + // form — including "could not confirm whether …" — is handled + // stem-agnostically by `questioned`, which is why this stem no longer + // enumerates negated verbs (ninth pass: that arm was unbound and vetoed "I + // did not check whether a newer head exists so already reviewed at …", a + // correct skip). + { + name: "hedge", + stem: + "\\b(?:unclear|unsure|uncertain|unknown|unverified|unconfirmed|ambiguous|inconclusive|not\\s+sure|not\\s+clear|no\\s+idea)" + + "\\s+(?:\\w+\\s+){0,3}that", + }, + // An assumption governing the clause with neither a negation nor a + // complementizer to hang on ("Possibly already reviewed at …", "Assuming this + // head was already reviewed at …", "I doubt this head was …"). Seventh pass: + // nine hedges outside the four original stems reached `already_reviewed`. + // Tenth pass added `guess`, which was reaching `already_reviewed` in the + // masking direction ("I am guessing this head was already reviewed at …"). + // + // Twelfth pass added the BELIEF and APPEARANCE predicates. `negation` below + // already lists believe / think / appears / suggests, but only reachable + // AFTER a negation word — so "no indication" and "cannot see" were caught + // while the bare hedging form ("I believe this head was …", "It appears this + // head was …") reached `already_reviewed` unvetoed: 12 phrasings of a run + // that never established the claim. That is the masking failure this whole + // mechanism exists to prevent, and it is specific to the shapes this + // function newly admits — master's mandatory-`for` pattern rejected the text + // incidentally, so there is no veto regression, only a widening to close. + // + // `doubt` carries a negative lookbehind because a NEGATED doubt is an + // affirmation: "there is no doubt this head was already reviewed at …" is the + // strongest possible statement of a correct skip, and an unguarded stem + // classified it `missing` (tenth pass: 5 such phrasings regressed against + // master). `in` covers the "not in doubt that …" form. + // + // The lookbehind spans up to two intervening words, because an adjective + // between the negation and the noun is the ORDINARY way these are written + // and "beyond reasonable doubt" is a stock idiom. An adjacency-only + // lookbehind restored none of them (eleventh pass: 6 of 6 regressed). + { + name: "assumption", + stem: + "\\b(?:possibly|probably|presumably|assum(?:e|es|ed|ing|ption|ptions)|presum(?:e|es|ed|ption|ptions)|apparently|guess(?:es|ing|ed)?" + + "|perhaps|maybe|likely|plausible|seem(?:s|ed)?|appear(?:s|ed)?|believ(?:e|es|ed)" + + "|think(?:s|ing)?|thought|suspect(?:s|ed)?|look(?:s|ed)?\\s+like" + + // Thirteenth pass: predicative adjectives whose ADVERB was already + // listed, and the epistemic NOUNS beside the listed `assumption`. Same + // stance, different part of speech; the adverb/noun asymmetry was an + // accident of how the list grew, not a distinction worth keeping. + "|probable|possible|conceivable|presumable|apparent" + + "|presumption|belief|impression|understanding" + + "|(? = GOVERNING_CUE_STEMS.map( + ({ name, stem }) => ({ name, re: new RegExp(`${stem}${CLAUSE_REACH}`, "i") }), +); + +// Which governing cue vetoes `before`, or null when none does. Every review +// pass so far has had to bisect the cues by hand to attribute a veto; this +// makes that attribution a single call, and keeps GOVERNING_CUES[].name +// load-bearing rather than decorative. +// Exported for attribution tests; not used by the classifier itself. +export function prReviewAlreadyReviewedVetoCue(before: string): string | null { + return GOVERNING_CUES.find(({ re }) => re.test(before))?.name ?? null; +} + +// Exported for direct table tests of the veto boundaries. +export function prReviewOutputHasAlreadyReviewedSkip(text: string): boolean { + // Markdown that may sit between tokens: whitespace, backticks, bold/italic. + const md = "[\\s`*_]*"; + const pattern = new RegExp( + // `+`, not `*`: the negation must be its own word, separated from `already` + // by whitespace or markdown. The same `+` class joins already/reviewed/at, + // so "**Already reviewed** at ``" (markdown closing before the sha) + // matches while `alreadyreviewed` cannot. + "(?\\b(?:not|never|wasn['\u2019]?t|weren['\u2019]?t|isn['\u2019]?t|aren['\u2019]?t|hasn['\u2019]?t)[\\s`*_]+(?:(?:yet|been|was|were|is|are)[\\s`*_]+){0,2})?" + + // `+` after `at` too: `at8b237675…` with no separator is not the clause. + "\\balready[\\s`*_]+reviewed[\\s`*_]+at[\\s`*_]+" + + // optional " for" (plain shape) and/or a "head"/"commit" noun + `(?[^\\s\`*_]{1,40}${md}for${md})?` + + // The anchor noun tolerates a determiner, an optional `sha` apposition, + // and punctuation separators: "at head: ", "at the head " and + // "at head sha " were all refused (false-missing direction, found by + // a parallel probe rather than by review). The separator quantifier stays + // `+`, so `head` with no separator is still not the clause. A bare + // `sha` noun is deliberately NOT admitted: it would widen the all-decimal + // surface, since "at sha 1234567" would then count as marker-qualified. + "(?(?:(?:the|this|current|latest)[\\s`*_]+)?(?:head|commit)(?:[\\s`*_]+sha)?[:=,\\s`*_]+)?" + + "(?[0-9a-f]{7,40})(?![0-9a-f])", + "gi", + ); + // Same-sentence scope: a clause boundary is any of . : ; , — – or a newline. + const clauseBefore = /[^.\n:;,\u2014\u2013]*$/; + for (const m of text.matchAll(pattern)) { + if (m.groups?.negated) continue; + // A sha is not a timestamp, and the comment above promises a sha-less + // claim is refused — so the code has to hold that, not merely document it + // (fourteenth pass). The compact ISO form `20260902T204153Z` contains a + // 7+ hex run (`20260902`) whose next character is not hex, so the anchor + // alone accepted it, as it did a bare `1234567`. An all-decimal token is + // therefore a commit only when a `for`/`head`/`commit` marker says so; + // that still admits a genuinely all-decimal abbreviated sha, which is + // ~3.7% of 7-character abbreviations and not negligible. + // + // Lowercase is re-checked here because the pattern's `i` flag exists for + // `Already`, not for the sha, and the attestation contract mandates + // lowercase hex. + const sha = m.groups?.sha ?? ""; + const shaIsLowerHex = /^[0-9a-f]+$/.test(sha); + const shaHasHexLetter = /[a-f]/.test(sha); + const shaMarked = Boolean(m.groups?.forMarker || m.groups?.nounMarker); + if (!shaIsLowerHex || (!shaHasHexLetter && !shaMarked)) { + // A rejected token must not consume the clause. `matchAll` yields + // non-overlapping matches, so `continue` here would swallow the only + // `already reviewed at` prefix and never reach a real sha later in the + // SAME clause — "already reviewed at 1234567 (sha `<40-hex>`)" (fifteenth + // pass). Re-scan the remainder of this clause, but require the later + // token to be marker-qualified (`sha`/`commit`/`head`), mirroring the + // rule the rejected token just failed: without that, any incidental hex + // word in the tail ("…and the deadbeef branch") would be read as the + // cited commit, which is the masking direction. + // Bounded by the clause cap: an unbounded slice copies O(n) per rejected + // token and `matchAll` yields one per `already reviewed at `, so + // the classifier went quadratic on run output — measured 12.8s at 547KB + // against 11ms before (Ally pass 16). `sameClause` stops at the first + // boundary anyway, so nothing past CLAUSE_SCOPE_CHARS was ever reachable. + const start = m.index + m[0].length; + const tail = text.slice(start, start + CLAUSE_SCOPE_CHARS); + const sameClause = /^[^.\n:;,—–]*/.exec(tail)?.[0] ?? ""; + const rescued = /\b(?:sha|commit|head)[\s`*_:=]+([0-9a-f]{7,40})(?![0-9a-f])/i.exec(sameClause); + const rescuedSha = rescued?.[1] ?? ""; + if (!/^[0-9a-f]+$/.test(rescuedSha) || !/[a-f]/.test(rescuedSha)) continue; + } + // A sentence-initial hedge adverb followed by its own comma ("Possibly, + // this head was …") is one clause, not two: the comma belongs to the + // adverb, and treating it as a boundary severs the adverb from the clause + // it governs — measured as a masking gap by the parallel probe. Only that + // one shape is rejoined; a comma anywhere else remains a boundary. + const window = text + .slice(Math.max(0, m.index - CLAUSE_SCOPE_CHARS), m.index) + .replace(LEADING_ADVERB_COMMA, "$1$2 "); + const before = clauseBefore.exec(window)?.[0] ?? ""; + if (GOVERNING_CUES.some(({ re }) => re.test(before))) continue; + return true; + } + return false; +} + export function evaluatePrReviewCompletionEvidence( contextSnapshot: Record | null | undefined, output: { @@ -9487,7 +9893,7 @@ export function evaluatePrReviewCompletionEvidence( if (/\bgh\s+pr\s+review\b[\s\S]{0,400}\bexit["']?\s*:\s*0\b/i.test(text)) { return { status: "posted_review" as const }; } - if (/\balready\s+reviewed\s+at\b[\s\S]{0,160}\bfor\b\s+[0-9a-f]{7,40}\b/i.test(text)) { + if (prReviewOutputHasAlreadyReviewedSkip(text)) { return { status: "already_reviewed" as const }; } if (