From 362a6155ba3f484b3f8ada44350f0d1630242cae Mon Sep 17 00:00:00 2001
From: JSONbored <49853598+JSONbored@users.noreply.github.com>
Date: Wed, 15 Jul 2026 05:12:01 -0700
Subject: [PATCH] review: unwind the retired unifiedComment feature key (#6103
follow-up)
The unified-comment bridge became the sole PR-comment renderer in #6128,
which left the unifiedComment ConvergedFeatureKey and its
LOOPOVER_REVIEW_UNIFIED_COMMENT env flag declared but functionally
inert. Since the drift-check gate requires every declared
LOOPOVER_REVIEW_* flag to be documented, and there was no working
precedent for a retired-but-still-required flag, this fully removes the
key/flag from the resolver, config-as-code examples, self-hosting docs,
and env declarations (regenerating worker-configuration.d.ts via
cf-typegen) rather than leaving stale references. Also fixes the
"signal table" description in docs.how-reviews-work.tsx, left over from
the earlier decision-drivers redesign (#6083), and a timing-flaky
GraphQL single-flight test unrelated to this change but caught by the
same full-suite run (crypto.subtle.digest is backed by a real OS thread
pool, so two concurrent callers with identical inputs can complete in
either order under load; stubbed with a deterministic pure-microtask
digest for the one test that exercises the race).
---
.env.example | 7 +---
.env.selfhost.example | 2 -
.loopover.yml.example | 6 +--
CONVERGENCE_RUNBOOK.md | 3 +-
.../src/routes/docs.github-app.tsx | 9 +----
.../src/routes/docs.how-reviews-work.tsx | 22 +++++-----
.../src/routes/docs.privacy-security.tsx | 1 -
.../docs.self-hosting-configuration.tsx | 12 +++---
apps/loopover-ui/src/routes/docs.tuning.tsx | 5 ---
config/examples/loopover.full.yml | 6 +--
.../loopover-engine/src/focus-manifest.ts | 19 +++++----
src/env.d.ts | 5 ---
src/queue/ci-resolution.ts | 4 +-
src/review/cutover-gate.ts | 2 +-
src/review/feature-activation.ts | 6 +--
src/review/inline-comments.ts | 4 +-
src/review/safety.ts | 2 +-
src/review/unified-comment-bridge.ts | 7 ----
src/review/unified-comment.ts | 3 +-
src/review/visual-wire.ts | 2 +-
test/unit/feature-activation.test.ts | 4 +-
test/unit/focus-manifest.test.ts | 9 ++---
test/unit/github-graphql-cache.test.ts | 40 +++++++++++++++----
.../linked-issue-satisfaction-run.test.ts | 2 -
test/unit/queue-3.test.ts | 2 -
test/unit/queue-4.test.ts | 40 ++++++++-----------
test/unit/queue-5.test.ts | 2 +-
test/unit/reputation-wiring.test.ts | 3 +-
test/unit/unified-comment-bridge.test.ts | 20 ----------
test/unit/visual-wire.test.ts | 2 +-
worker-configuration.d.ts | 4 +-
wrangler.jsonc | 1 -
32 files changed, 103 insertions(+), 153 deletions(-)
diff --git a/.env.example b/.env.example
index 424be8605c..72095bc8da 100644
--- a/.env.example
+++ b/.env.example
@@ -19,7 +19,7 @@
# --- Scope (per-repo cutover allowlist) -------------------------------------
# Comma-separated owner/repo names allowed to run the per-PR review features
-# (SAFETY, GROUNDING, RAG, REPUTATION, UNIFIED_COMMENT). A per-PR feature runs on
+# (SAFETY, GROUNDING, RAG, REPUTATION). A per-PR feature runs on
# a repo only if its own flag is ON *and* the repo is listed here. Empty = no
# repos, so every per-PR feature stays dormant regardless of the flags below.
# Case-insensitive, trimmed; stray commas ignored.
@@ -107,11 +107,6 @@ LOOPOVER_REVIEW_ENRICHMENT=false
LOOPOVER_REVIEW_REPUTATION=false
# GITTENSORY_REVIEW_REPUTATION=false # no longer read (removed by #4777)
-# Renders the public PR comment as one in-place unified comment instead of the
-# legacy multi-panel comment. OFF keeps the legacy comment byte-identical.
-LOOPOVER_REVIEW_UNIFIED_COMMENT=false
-# GITTENSORY_REVIEW_UNIFIED_COMMENT=false # no longer read (removed by #4777)
-
# --- Global capabilities (NOT scoped by LOOPOVER_REVIEW_REPOS) -------------
# Observability (read-only): cron anomaly scan over the gate-block ledger emits
diff --git a/.env.selfhost.example b/.env.selfhost.example
index 2636d54870..2c15a6a7a5 100644
--- a/.env.selfhost.example
+++ b/.env.selfhost.example
@@ -71,8 +71,6 @@ REDIS_URL=redis://redis:6379
SELFHOST_DEPLOYMENT_MODE=dry-run
LOOPOVER_REVIEW_REPOS=owner/repo
# GITTENSORY_REVIEW_REPOS=owner/repo # no longer read (removed by #4777)
-LOOPOVER_REVIEW_UNIFIED_COMMENT=true
-# GITTENSORY_REVIEW_UNIFIED_COMMENT=true # no longer read (removed by #4777)
LOOPOVER_REVIEW_SAFETY=true
# GITTENSORY_REVIEW_SAFETY=true # no longer read (removed by #4777)
LOOPOVER_REVIEW_GROUNDING=true
diff --git a/.loopover.yml.example b/.loopover.yml.example
index 0279f4e4fb..722c5068f4 100644
--- a/.loopover.yml.example
+++ b/.loopover.yml.example
@@ -483,7 +483,6 @@ review:
# max_files: 25
# Unified-comment display toggles (#1957 / #1955 / #2069). Bool | null. Default: null/false — byte-identical.
- # Only take effect when the `features.unifiedComment` feature is enabled.
# changed_files_summary: false
# When true, the unified review comment gains a deterministic "Changed files" summary table.
# effort_score: false
@@ -1120,7 +1119,7 @@ settings:
# intentRouting: false # Closed-set intent classifier for unrecognized @loopover mentions -> existing Q&A commands only. Ollama-ONLY, same as chatQa (never uses chatQaFrontierFallback). Co-requisite: commandRateLimitPolicy: hold. Default: false.
# Per-repo activation overrides for the converged review features that ship behind a deployment-wide
-# LOOPOVER_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety/grounding/e2eTests/screenshots/
+# LOOPOVER_REVIEW_* env kill-switch (rag/reputation/safety/grounding/e2eTests/screenshots/
# improvementSignal). Each key is `true` (force on for this repo, subject to the env flag still being
# enabled), `false` (force off), or omitted (falls back to the LOOPOVER_REVIEW_REPOS allowlist default --
# an operator who sets nothing keeps today's behavior). Exception: `safety` is force-on-only -- an untrusted
@@ -1134,14 +1133,13 @@ settings:
# `review.visual.enabled: false` as an always-available additional force-off); this key only answers "does
# capture run for this repo at all," the same question its siblings answer for their own feature.
# `e2eTests` (#4190) and `improvementSignal` (#4738, foundation phase of the #4737 PR-improvement-signal
-# epic) remain plain symmetric overrides like rag/reputation/unifiedComment -- neither carries a
+# epic) remain plain symmetric overrides like rag/reputation -- neither carries a
# security-hardening or full-file-fetch rationale that would justify a force-on/force-off asymmetry.
# `improvementSignal` is activation wiring only for now -- no tier reads the resolved
# value yet.
# features:
# rag: true
# reputation: false
-# unifiedComment: true
# safety: true
# grounding: true
# e2eTests: false
diff --git a/CONVERGENCE_RUNBOOK.md b/CONVERGENCE_RUNBOOK.md
index 65fbb62f54..a855a806e7 100644
--- a/CONVERGENCE_RUNBOOK.md
+++ b/CONVERGENCE_RUNBOOK.md
@@ -21,7 +21,7 @@ The old vendor/embed plan is obsolete. The review system now lives in **gittenso
- **Single project:** gittensory is the only source repo for the converged review system.
- **Native port:** review features live under `src/review/**`, `src/queue/processors.ts`, and related first-party modules.
-- **Public comment path:** the unified in-place PR comment is driven by the native bridge and the `LOOPOVER_REVIEW_UNIFIED_COMMENT` flag.
+- **Public comment path:** the unified in-place PR comment is rendered unconditionally by the native bridge — it is the only comment-rendering path (the legacy multi-panel renderer and its `LOOPOVER_REVIEW_UNIFIED_COMMENT` flag were retired).
- **Infra model:** D1 / Queue / AI / optional Vectorize / optional R2 / optional Browser bindings are declared directly in gittensory.
- **Config model:** rollout is controlled by `LOOPOVER_REVIEW_*` flags plus the per-repo allowlist `LOOPOVER_REVIEW_REPOS`.
- **Parity model:** parity is measured as a shadow/deploy-time comparison against authoritative legacy audit rows; local checkout validation proves structure and safety, not historical decision identity.
@@ -48,7 +48,6 @@ Those are operator actions. This repo should document them clearly and avoid imp
Primary native review flags and surfaces:
-- `LOOPOVER_REVIEW_UNIFIED_COMMENT` — single public PR comment
- `LOOPOVER_REVIEW_SAFETY` — prompt-injection defang + secret scan
- `LOOPOVER_REVIEW_GROUNDING` — CI + full-file grounding
- `LOOPOVER_REVIEW_RAG` — retrieval-augmented context
diff --git a/apps/loopover-ui/src/routes/docs.github-app.tsx b/apps/loopover-ui/src/routes/docs.github-app.tsx
index e90b6814e9..e848ce169d 100644
--- a/apps/loopover-ui/src/routes/docs.github-app.tsx
+++ b/apps/loopover-ui/src/routes/docs.github-app.tsx
@@ -306,11 +306,6 @@ review:
new / burst / low-reputation submitter to a deterministic-only review. Internal-only,
never surfaced publicly. Per-PR.
-
- LOOPOVER_REVIEW_UNIFIED_COMMENT — renders the public PR comment as one
- in-place unified comment instead of the legacy multi-panel comment. Per-PR; flag-off keeps
- the legacy comment byte-identical.
-
LOOPOVER_REVIEW_OPS — read-only observability: a cron anomaly scan over your
own review-outcome data plus a bearer-gated stats aggregate. Global (not scoped by the
@@ -338,9 +333,9 @@ review:
diff --git a/apps/loopover-ui/src/routes/docs.how-reviews-work.tsx b/apps/loopover-ui/src/routes/docs.how-reviews-work.tsx
index 349f976f6d..aab9b0ac8e 100644
--- a/apps/loopover-ui/src/routes/docs.how-reviews-work.tsx
+++ b/apps/loopover-ui/src/routes/docs.how-reviews-work.tsx
@@ -230,8 +230,7 @@ function HowReviewsWork() {
3. The unified review comment
The result is rendered as one in-place comment on the PR — updated in place
- on each push rather than stacked — when LOOPOVER_REVIEW_UNIFIED_COMMENT is on
- for the repo. It has three parts, top to bottom:
+ on each push rather than stacked. It has three parts, top to bottom:
@@ -240,14 +239,16 @@ function HowReviewsWork() {
scans first.
- The signal table — a compact row-per-signal summary: each dimension that
- ran, its state (pass / advisory / block), and a short reason. This is the at-a-glance map
- of why the verdict came out the way it did.
+ Decision drivers — a short, always-visible checklist of the signals that
+ actually decided the verdict: the AI code review and the gate result. This is the
+ at-a-glance answer to why the verdict came out the way it did.
- Collapsibles — expandable sections for the detail behind each signal: the
- AI review notes, the slop warnings, duplicate matches, manifest findings. Folded away by
- default so the comment stays short, opened when a reader wants the evidence.
+ Collapsibles — expandable sections for everything else: a "Context &
+ advisory signals" table (linked issue, related work, validation posture, contributor
+ context — signals that inform but never block the verdict), the AI review notes, slop
+ warnings, duplicate matches, manifest findings. Folded away by default so the comment
+ stays short, opened when a reader wants the evidence.
@@ -265,7 +266,10 @@ function HowReviewsWork() {
4. The signals behind a verdict
-
Each row in the signal table comes from a named finding. The common ones you will see:
+
+ Each row in the advisory signals table comes from a named finding. The common ones you will
+ see:
+
The features: block above overrides a deployment-wide{" "}
- LOOPOVER_REVIEW_* flag (rag, reputation, unifiedComment, safety) for this one
- repo, with three states per key: true forces the capability on for this repo
- (still subject to the env flag itself being enabled — it can never turn on a capability the
- operator has fully disabled at the deployment level); false forces it off for
- this repo regardless of the env flag; and omitting the key entirely falls back to the{" "}
+ LOOPOVER_REVIEW_* flag (rag, reputation, safety) for this one repo, with three
+ states per key: true forces the capability on for this repo (still subject to
+ the env flag itself being enabled — it can never turn on a capability the operator has fully
+ disabled at the deployment level); false forces it off for this repo regardless
+ of the env flag; and omitting the key entirely falls back to the{" "}
LOOPOVER_REVIEW_REPOS allowlist default, i.e. today's behavior for an operator
who hasn't set anything here. See Tuning your reviews for the
full LOOPOVER_REVIEW_* flag list this overrides.
diff --git a/apps/loopover-ui/src/routes/docs.tuning.tsx b/apps/loopover-ui/src/routes/docs.tuning.tsx
index 3d58872c32..dd2e5f438d 100644
--- a/apps/loopover-ui/src/routes/docs.tuning.tsx
+++ b/apps/loopover-ui/src/routes/docs.tuning.tsx
@@ -194,11 +194,6 @@ function Tuning() {
reputation proceeds normally. Never surfaced publicly — no comment, label, or check shows
reputation. Per-PR.
-
- LOOPOVER_REVIEW_UNIFIED_COMMENT — renders the public PR comment as one
- in-place unified comment instead of the legacy multi-panel comment. Per-PR. With the flag
- off, the legacy comment is byte-identical.
-
LOOPOVER_REVIEW_ENRICHMENT — runs the review-enrichment analyzer registry
(duplication, churn hotspots, blame links, approval integrity, undocumented exports, and
diff --git a/config/examples/loopover.full.yml b/config/examples/loopover.full.yml
index aa5cd4e607..09608d9545 100644
--- a/config/examples/loopover.full.yml
+++ b/config/examples/loopover.full.yml
@@ -497,7 +497,6 @@ review:
# max_files: 25
# Unified-comment display toggles (#1957 / #1955 / #2069). Bool | null. Default: null/false — byte-identical.
- # Only take effect when the `features.unifiedComment` feature is enabled.
# changed_files_summary: false
# When true, the unified review comment gains a deterministic "Changed files" summary table.
# effort_score: false
@@ -1134,7 +1133,7 @@ settings:
# intentRouting: false # Closed-set intent classifier for unrecognized @loopover mentions -> existing Q&A commands only. Ollama-ONLY, same as chatQa (never uses chatQaFrontierFallback). Co-requisite: commandRateLimitPolicy: hold. Default: false.
# Per-repo activation overrides for the converged review features that ship behind a deployment-wide
-# LOOPOVER_REVIEW_* env kill-switch (rag/reputation/unifiedComment/safety/grounding/e2eTests/screenshots/
+# LOOPOVER_REVIEW_* env kill-switch (rag/reputation/safety/grounding/e2eTests/screenshots/
# improvementSignal). Each key is `true` (force on for this repo, subject to the env flag still being
# enabled), `false` (force off), or omitted (falls back to the LOOPOVER_REVIEW_REPOS allowlist default --
# an operator who sets nothing keeps today's behavior). Exception: `safety` is force-on-only -- an untrusted
@@ -1148,14 +1147,13 @@ settings:
# `review.visual.enabled: false` as an always-available additional force-off); this key only answers "does
# capture run for this repo at all," the same question its siblings answer for their own feature.
# `e2eTests` (#4190) and `improvementSignal` (#4738, foundation phase of the #4737 PR-improvement-signal
-# epic) remain plain symmetric overrides like rag/reputation/unifiedComment -- neither carries a
+# epic) remain plain symmetric overrides like rag/reputation -- neither carries a
# security-hardening or full-file-fetch rationale that would justify a force-on/force-off asymmetry.
# `improvementSignal` is activation wiring only for now -- no tier reads the resolved
# value yet.
# features:
# rag: true
# reputation: false
-# unifiedComment: true
# safety: true
# grounding: true
# e2eTests: false
diff --git a/packages/loopover-engine/src/focus-manifest.ts b/packages/loopover-engine/src/focus-manifest.ts
index fd93cfdf4f..ce6cb93600 100644
--- a/packages/loopover-engine/src/focus-manifest.ts
+++ b/packages/loopover-engine/src/focus-manifest.ts
@@ -261,7 +261,7 @@ export type CopycatGateMode = "off" | "warn" | "label" | "block";
// level `false` behaves like any other plain override with no floor/ceiling. This is activation wiring only
// -- no tier reads the resolved value yet (sibling sub-issues #4739-#4746 build the deterministic/LLM/panel
// behavior that will gate on it).
-export const CONVERGED_FEATURE_KEYS = ["rag", "reputation", "unifiedComment", "safety", "grounding", "e2eTests", "screenshots", "improvementSignal"] as const;
+export const CONVERGED_FEATURE_KEYS = ["rag", "reputation", "safety", "grounding", "e2eTests", "screenshots", "improvementSignal"] as const;
export type ConvergedFeatureKey = (typeof CONVERGED_FEATURE_KEYS)[number];
/** Per-repo activation overrides for the converged review features (`features:` block). `true`/`false` force the
@@ -542,15 +542,15 @@ export type FocusManifestReviewConfig = {
* otherwise) — this is an ADDITIONAL opt-in on top of `review.inline_comments`, not a replacement gate.
* null/false (default, absent) = no suggestion blocks = byte-identical behavior. (#1956) */
suggestions: boolean | null;
- /** `review.changed_files_summary`: when true, the unified review comment (only rendered at all when the
- * `unifiedComment` convergence feature is on) gains a deterministic, no-AI "Changed files" collapsible: one
- * row per file category (source/test/docs/config/generated), with file counts and +/- totals, via the
- * existing `classifyChangedFile` classifier (`src/review/changed-files-classify.ts`, built for this table
- * under #2143). null/false (default, absent) = no changed-files section = byte-identical behavior. (#1957) */
+ /** `review.changed_files_summary`: when true, the unified review comment gains a deterministic, no-AI
+ * "Changed files" collapsible: one row per file category (source/test/docs/config/generated), with file
+ * counts and +/- totals, via the existing `classifyChangedFile` classifier
+ * (`src/review/changed-files-classify.ts`, built for this table under #2143). null/false (default, absent)
+ * = no changed-files section = byte-identical behavior. (#1957) */
changedFilesSummary: boolean | null;
- /** `review.effort_score`: when true, the unified review comment (only rendered when the `unifiedComment`
- * convergence feature is on) gains a compact "review effort: N/5 (~M min)" chip — a deterministic, no-AI
- * complexity/time estimate from `estimateReviewEffort` (`src/review/review-effort.ts`), weighting each
+ /** `review.effort_score`: when true, the unified review comment gains a compact "review effort: N/5 (~M min)"
+ * chip — a deterministic, no-AI complexity/time estimate from `estimateReviewEffort`
+ * (`src/review/review-effort.ts`), weighting each
* changed file's added lines by its category (source costs most; generated/vendored/lockfiles cost least)
* plus a fixed per-file overhead. Mirrors `changedFilesSummary` exactly: same table, same deterministic
* source, same display-only (never touches the AI prompt) shape. null/false (default, absent) = no chip =
@@ -1021,7 +1021,6 @@ const EMPTY_FEATURES_CONFIG: FocusManifestFeaturesConfig = {
present: false,
rag: null,
reputation: null,
- unifiedComment: null,
safety: null,
grounding: null,
e2eTests: null,
diff --git a/src/env.d.ts b/src/env.d.ts
index 5a3ceb9d72..e2542d3493 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -302,11 +302,6 @@ declare global {
* secret, never a public var. When absent, BYOK is unavailable and review uses the configured instance
* reviewer when available. */
TOKEN_ENCRYPTION_SECRET?: string;
- /** #6103: retired -- the unified-comment bridge is now the ONLY PR-comment renderer unconditionally (the
- * legacy `buildPublicPrIntelligenceComment` panel this flag used to switch away from was deleted, having
- * no remaining production caller). No longer read; kept only so an operator's existing deployment config
- * setting it doesn't error. Safe to remove from any env once noticed. */
- LOOPOVER_REVIEW_UNIFIED_COMMENT?: string;
/** Inline comments (#inline-comments): when truthy (AND the repo is in LOOPOVER_REVIEW_REPOS AND the repo's
* `.loopover.yml` sets `review.inline_comments: true`), the AI reviewer ALSO leaves quiet, NON-BLOCKING
* inline comments on specific changed lines, layered on top of the decision summary. Default OFF —
diff --git a/src/queue/ci-resolution.ts b/src/queue/ci-resolution.ts
index 9cba4dd293..1376c2b777 100644
--- a/src/queue/ci-resolution.ts
+++ b/src/queue/ci-resolution.ts
@@ -348,8 +348,8 @@ export function refreshLiveMergeState(
// refreshLiveMergeState's #4220 doc comment above prohibits for this act-boundary-adjacent disposition input.
// So this only reuses a memoized value when its key is ALSO in forcedMergeStateKeys/forcedCiAggregateKeys --
// i.e. it was written by a FORCED (genuinely-live-this-pass) call, never by a cached-path reader. On a genuine
-// miss (no forced call ran yet this pass, e.g. unifiedCommentAllowed was false) this falls through to a REAL
-// live refresh, so behavior can only ever improve (fewer calls) over the pre-fix code, never go staler.
+// miss (no forced call has run yet this pass) this falls through to a REAL live refresh, so behavior can only
+// ever improve (fewer calls) over the pre-fix code, never go staler.
export function reuseOrRefreshLiveMergeState(
env: Env,
repoFullName: string,
diff --git a/src/review/cutover-gate.ts b/src/review/cutover-gate.ts
index e4c6055cbf..60c361f26b 100644
--- a/src/review/cutover-gate.ts
+++ b/src/review/cutover-gate.ts
@@ -1,7 +1,7 @@
// Convergence (cutover) per-repo gate: an allowlist that activates the PER-PR converged review features one
// repo at a time, so the cutover can be rolled forward (and rolled back) on a single repo without flipping the
// global flags off for everyone. Each per-PR converged feature ALREADY has a global switch (LOOPOVER_REVIEW_SAFETY /
-// _GROUNDING / _RAG / _REPUTATION, LOOPOVER_REVIEW_UNIFIED_COMMENT); this adds a SECOND, repo-scoped gate that must
+// _GROUNDING / _RAG / _REPUTATION); this adds a SECOND, repo-scoped gate that must
// ALSO pass for the feature to run on a given PR's repo.
//
// Single env var: LOOPOVER_REVIEW_REPOS — a comma-separated allowlist of repo full-names
diff --git a/src/review/feature-activation.ts b/src/review/feature-activation.ts
index 766a0d4d8d..a5b5a5a1a4 100644
--- a/src/review/feature-activation.ts
+++ b/src/review/feature-activation.ts
@@ -13,7 +13,7 @@
//
// `resolveFeatureActivation` below is now the ONE pure core every one of those precedence shapes reduces to.
// `resolveConvergedFeature` and `resolveManifestOnlyFeature` are the two thin adapters over it in actual use:
-// - `resolveConvergedFeature` — the `features:`-block keys (rag/reputation/unifiedComment/safety/grounding/
+// - `resolveConvergedFeature` — the `features:`-block keys (rag/reputation/safety/grounding/
// e2eTests/screenshots): env kill-switch → per-repo `features:` override → `LOOPOVER_REVIEW_REPOS`
// allowlist default. Safety, grounding, and screenshots are the named exceptions this shape has; see
// `FEATURE_MODE` below.
@@ -35,7 +35,6 @@ import { isImprovementSignalEnabled } from "./improvement-signal-wire";
import { isRagEnabled } from "./rag-wire";
import { isReputationEnabled } from "./reputation-wire";
import { isSafetyEnabled } from "./safety";
-import { isUnifiedReviewCommentEnabled } from "./unified-comment-bridge";
import { isScreenshotsEnabled } from "./visual-wire";
import type { ConvergedFeatureKey, FocusManifest } from "../signals/focus-manifest";
import { loadRepoFocusManifest } from "../signals/focus-manifest-loader";
@@ -44,7 +43,7 @@ import { loadRepoFocusManifest } from "../signals/focus-manifest-loader";
* The four per-feature activation precedence shapes actually in use across loopover's advisory review
* capabilities (#4616):
* - `"standard"`: `override` fully controls (`true` forces on, `false` forces off); `null` (unset) falls back
- * to `allowlisted`. rag / reputation / unifiedComment / e2eTests / improvementSignal.
+ * to `allowlisted`. rag / reputation / e2eTests / improvementSignal.
* - `"forceOnOnly"`: `override` can only force ON (bypassing the allowlist); an untrusted `false` is "no
* opinion" and falls through to `allowlisted` — for a feature where a lower-trust, repo-controlled override
* must never be able to silently defeat the operator's own enablement. safety (#2269).
@@ -80,7 +79,6 @@ export function resolveFeatureActivation(globalFlagOn: boolean, override: boolea
const FEATURE_GLOBAL_FLAG: Record boolean> = {
rag: isRagEnabled,
reputation: isReputationEnabled,
- unifiedComment: isUnifiedReviewCommentEnabled,
safety: isSafetyEnabled,
grounding: isGroundingEnabled,
e2eTests: isE2eTestGenerationEnabled,
diff --git a/src/review/inline-comments.ts b/src/review/inline-comments.ts
index 803f61fde4..d131d82b79 100644
--- a/src/review/inline-comments.ts
+++ b/src/review/inline-comments.ts
@@ -26,7 +26,7 @@ import { errorMessage } from "../utils/json";
/** True when the operator enabled inline comments globally. Flag-OFF (default) ⇒ the caller never asks the model
* for inline findings, so this module is never reached. Truthy follows the codebase convention (same regex as
- * isUnifiedReviewCommentEnabled / isSafetyEnabled). */
+ * isRagEnabled / isSafetyEnabled). */
export function isInlineCommentsEnabled(env: {
LOOPOVER_REVIEW_INLINE_COMMENTS?: string | undefined;
}): boolean {
@@ -40,7 +40,7 @@ export function isInlineCommentsEnabled(env: {
* `.loopover.yml` `review.inlineComments` override (`true`/`false`) now FULLY controls the feature by itself
* — a repo can turn this on without needing the LOOPOVER_REVIEW_REPOS cutover allowlist at all. (3)
* `manifestToggle` unset (`undefined`) preserves this feature's ORIGINAL design exactly: unlike
- * rag/reputation/safety/unifiedComment/grounding (which already fall back to the cutover allowlist when their
+ * rag/reputation/safety/grounding (which already fall back to the cutover allowlist when their
* manifest field is unset), inline comments have always required an EXPLICIT per-repo opt-in — being on the
* allowlist alone was never sufficient, so this stays `false` regardless of the allowlist, byte-identical to
* every repo's behavior before this change (now expressed as `resolveManifestOnlyFeature`'s `"manifestOnly"`
diff --git a/src/review/safety.ts b/src/review/safety.ts
index aa75e3e90b..aeff68c90f 100644
--- a/src/review/safety.ts
+++ b/src/review/safety.ts
@@ -3,7 +3,7 @@
//
// Single env switch: LOOPOVER_REVIEW_SAFETY. Default OFF (unset/"false") — when OFF none of the helpers here
// alter inputs or findings, so the review path is byte-identical to today. Truthy follows the codebase
-// convention (`/^(1|true|yes|on)$/i`, same as isUnifiedReviewCommentEnabled / isEnabled).
+// convention (`/^(1|true|yes|on)$/i`, same as isRagEnabled / isEnabled).
import type { AdvisoryFinding } from "../types";
import { neutralizePromptInjection, safeReviewTitle } from "./prompt-injection";
diff --git a/src/review/unified-comment-bridge.ts b/src/review/unified-comment-bridge.ts
index b2afee246a..426da542c6 100644
--- a/src/review/unified-comment-bridge.ts
+++ b/src/review/unified-comment-bridge.ts
@@ -948,10 +948,3 @@ export function buildClosedUnifiedCommentBody(args: { repoFullName: string; pull
footerMarkdown: args.footerMarkdown,
});
}
-
-/** Truthy-env flag check, matching the codebase convention (e.g. SCORING_TIME_DECAY_ENABLED). */
-export function isUnifiedReviewCommentEnabled(env: {
- LOOPOVER_REVIEW_UNIFIED_COMMENT?: string | undefined;
-}): boolean {
- return /^(1|true|yes|on)$/i.test((env.LOOPOVER_REVIEW_UNIFIED_COMMENT ?? "").trim());
-}
diff --git a/src/review/unified-comment.ts b/src/review/unified-comment.ts
index 240bf7a907..d9b751cbde 100644
--- a/src/review/unified-comment.ts
+++ b/src/review/unified-comment.ts
@@ -1,4 +1,5 @@
-// Unified PR review comment renderer (convergence — see docs/LOOPOVER_REVIEW_UNIFIED_COMMENT.md).
+// Unified PR review comment renderer (convergence). The sole comment-rendering path since #6103 — no
+// operator flag selects it anymore.
//
// Produces ONE in-place comment in the loopover SHAPE (colored alert sidebar + readiness
// signal table + collapsibles + re-run + earning footer) with reviewbot's deep review folded
diff --git a/src/review/visual-wire.ts b/src/review/visual-wire.ts
index 04508b53aa..c3478381d0 100644
--- a/src/review/visual-wire.ts
+++ b/src/review/visual-wire.ts
@@ -2,7 +2,7 @@
//
// Single env switch: LOOPOVER_REVIEW_SCREENSHOTS. Default OFF (unset/"false") — when OFF the processor
// never calls buildCapture, so the review path is byte-identical to today. Truthy follows the codebase
-// convention (`/^(1|true|yes|on)$/i`, same as isSafetyEnabled / isUnifiedReviewCommentEnabled).
+// convention (`/^(1|true|yes|on)$/i`, same as isSafetyEnabled / isRagEnabled).
//
// `screenshots` is a `ConvergedFeatureKey` (#4616): per-repo activation — the global flag here AND (a per-repo
// `.loopover.yml` `features.screenshots` override OR the `LOOPOVER_REVIEW_REPOS` cutover allowlist
diff --git a/test/unit/feature-activation.test.ts b/test/unit/feature-activation.test.ts
index 87c54b3bf7..18610a03a8 100644
--- a/test/unit/feature-activation.test.ts
+++ b/test/unit/feature-activation.test.ts
@@ -10,7 +10,6 @@ const REPO = "JSONbored/gittensory";
const FLAG: Record = {
rag: "LOOPOVER_REVIEW_RAG",
reputation: "LOOPOVER_REVIEW_REPUTATION",
- unifiedComment: "LOOPOVER_REVIEW_UNIFIED_COMMENT",
safety: "LOOPOVER_REVIEW_SAFETY",
grounding: "LOOPOVER_REVIEW_GROUNDING",
e2eTests: "LOOPOVER_REVIEW_E2E_TESTS",
@@ -26,7 +25,6 @@ function manifestWith(features: Partial>):
present: false,
rag: null,
reputation: null,
- unifiedComment: null,
safety: null,
grounding: null,
e2eTests: null,
@@ -205,7 +203,7 @@ describe("resolveConvergedFeature — screenshots remain allowlist-bound", () =>
describe("resolveConvergedFeature — improvementSignal is a plain symmetric override (#4738)", () => {
// The full resolution matrix the #4738 acceptance criteria calls out explicitly: env off; env on + no
// override; env on + repo true; env on + repo false. improvementSignal has no safety/grounding-style
- // asymmetry, so this mirrors the generic "standard mode" shape rag/reputation/unifiedComment/e2eTests use.
+ // asymmetry, so this mirrors the generic "standard mode" shape rag/reputation/e2eTests use.
it("is off when the global env flag is off, regardless of a per-repo override or the allowlist (env off)", () => {
const e = env({ LOOPOVER_REVIEW_REPOS: REPO }); // LOOPOVER_REVIEW_IMPROVEMENT_SIGNAL unset
expect(resolveConvergedFeature(e, manifestWith({ improvementSignal: true }), "improvementSignal", REPO)).toBe(false);
diff --git a/test/unit/focus-manifest.test.ts b/test/unit/focus-manifest.test.ts
index 74729bbfde..310d59fcbf 100644
--- a/test/unit/focus-manifest.test.ts
+++ b/test/unit/focus-manifest.test.ts
@@ -416,7 +416,6 @@ describe(".loopover.yml.example field-exhaustiveness (#1670)", () => {
const FEATURES_FIELD_TOKENS = {
rag: "rag:",
reputation: "reputation:",
- unifiedComment: "unifiedComment:",
safety: "safety:",
grounding: "grounding:",
e2eTests: "e2eTests:",
@@ -890,7 +889,7 @@ describe("compileFocusManifestPolicy", () => {
gate: { present: false, enabled: null, checkMode: null, pack: null, linkedIssue: null, duplicates: null, readinessMode: null, readinessMinScore: null, slopMode: null, slopMinScore: null, slopAiAdvisory: null, sizeMode: null, sizeMaxFiles: null, sizeMaxLines: null, lockfileIntegrityMode: null, aiReviewMode: null, aiReviewByok: null, aiReviewProvider: null, aiReviewModel: null, aiReviewAllAuthors: null, aiReviewCloseConfidence: null, aiReviewLowConfidenceDisposition: null, aiReviewCombine: null, aiReviewOnMerge: null, aiReviewReviewers: null, mergeReadiness: null, selfAuthoredLinkedIssue: null, linkedIssueSatisfaction: null, manifestPolicy: null, dryRun: null, firstTimeContributorGrace: null, premergeContentRecheck: null, requireFreshRebaseWindowMinutes: null, claMode: null, claConsentPhrase: null, claCheckRunName: null, claCheckRunAppSlug: null, expectedCiContexts: null, advisoryCheckRuns: null, aiJudgmentBlockersMode: null, copycatMode: null, copycatMinScore: null },
settings: {},
review: { present: false, footerText: null, note: null, fields: {}, enrichmentAnalyzers: {}, profile: null, tone: null, securityFocus: null, inlineComments: null, fixHandoff: null, autoMergeSummary: null, suggestions: null, changedFilesSummary: null, effortScore: null, impactMap: null, cultureProfile: null, selftune: null, reviewMemory: null, findingCategories: null, inlineCommentsPerCategory: null, minFindingSeverity: null, maxFindings: { blockers: null, nits: null }, commentVerbosity: null, e2eTestDelivery: null, e2eTestAutoTrigger: null, pathInstructions: [], instructions: null, excludePaths: [], pathFilters: [], preMergeChecks: [], autoReview: { ...EMPTY_AUTO_REVIEW_CONFIG }, aiModel: { ...EMPTY_SELF_HOST_AI_MODEL_CONFIG }, visual: { ...EMPTY_VISUAL_CONFIG }, linkedIssueSatisfaction: null, sharedConfigSource: null },
- features: { present: false, rag: null, reputation: null, unifiedComment: null, safety: null, grounding: null, e2eTests: null, screenshots: null, improvementSignal: null },
+ features: { present: false, rag: null, reputation: null, safety: null, grounding: null, e2eTests: null, screenshots: null, improvementSignal: null },
experimental: { present: false, gittensor: null },
contentLane: { present: false, entryFileGlob: null, providerFileGlob: null, artifactGlob: null, collectionField: null, maxAppendedEntries: null, duplicateKeyFields: [], validatorId: null },
repoDocGeneration: { present: false, enabled: false, scope: ["agents"], allowOverwriteExisting: false, refreshIntervalDays: 7 },
@@ -1674,13 +1673,13 @@ describe("parseFocusManifest gate config", () => {
});
it("parses the features: block (per-repo converged-feature toggles), round-trips it, and makes the manifest present", () => {
- const m = parseFocusManifest({ features: { rag: true, reputation: false, unifiedComment: true } });
+ const m = parseFocusManifest({ features: { rag: true, reputation: false, safety: true } });
expect(m.present).toBe(true);
expect(m.features.present).toBe(true);
expect(m.features.rag).toBe(true);
expect(m.features.reputation).toBe(false);
- expect(m.features.unifiedComment).toBe(true);
- expect(m.features.safety).toBeNull(); // unset stays null (⇒ allowlist default at resolve time)
+ expect(m.features.safety).toBe(true);
+ expect(m.features.grounding).toBeNull(); // unset stays null (⇒ allowlist default at resolve time)
// Round-trips through featuresConfigToJson → parseFocusManifest unchanged.
expect(parseFocusManifest({ features: featuresConfigToJson(m.features) }).features).toEqual(m.features);
// A non-boolean value warns and is dropped (stays null); a non-mapping warns.
diff --git a/test/unit/github-graphql-cache.test.ts b/test/unit/github-graphql-cache.test.ts
index feaf1d4ae1..5afe4bfeb3 100644
--- a/test/unit/github-graphql-cache.test.ts
+++ b/test/unit/github-graphql-cache.test.ts
@@ -51,6 +51,25 @@ function installMemoryResponseCache(): Map {
return store;
}
+/** graphqlCacheKey (src/github/graphql-cache.ts) awaits two real `crypto.subtle.digest` calls before the
+ * single-flight map is checked. Real WebCrypto digests are backed by a genuine OS thread pool, so two
+ * concurrent callers with IDENTICAL inputs can still complete their digest awaits in either relative
+ * order depending on real thread scheduling -- verified empirically (~11% failure rate over 500 trials in
+ * a tight Node loop, zero CPU contention needed) BEFORE this stub existed. Swapping in a pure-JS,
+ * deterministic digest removes that real-thread dependency: both calls now resolve via plain microtasks,
+ * whose relative ordering is governed only by the single-threaded JS event loop and is NOT sensitive to
+ * system load. This makes the coalescing race deterministic without touching the single-flight logic
+ * itself or changing what the test proves. Must be paired with `digestSpy.mockRestore()` -- this file's
+ * `afterEach` only unstubs `vi.stubGlobal`/env, not `vi.spyOn` mocks. */
+function stubDeterministicDigest() {
+ return vi.spyOn(crypto.subtle, "digest").mockImplementation(async (_algorithm, data) => {
+ const bytes = ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data as ArrayBuffer);
+ let hash = 0;
+ for (const byte of bytes) hash = (hash * 31 + byte) >>> 0;
+ return new Uint8Array(Array.from({ length: 32 }, (_, i) => (hash >>> (i % 4) * 8) & 0xff)).buffer;
+ });
+}
+
afterEach(() => {
clearGitHubResponseCacheForTest();
clearGitHubGraphQlCacheForTest();
@@ -177,6 +196,7 @@ describe("fetchCachedGitHubGraphQl", () => {
it("single-flights concurrent cold misses for the same query", async () => {
installMemoryResponseCache();
+ const digestSpy = stubDeterministicDigest();
let fetches = 0;
vi.stubGlobal("fetch", async () => {
fetches += 1;
@@ -184,14 +204,18 @@ describe("fetchCachedGitHubGraphQl", () => {
return Response.json({ data: { repository: { issues: { totalCount: 1 } } } });
});
- const [a, b] = await Promise.all([
- fetchCachedGitHubGraphQl(TOTALS_QUERY, "token-a"),
- fetchCachedGitHubGraphQl(TOTALS_QUERY, "token-a"),
- ]);
-
- expect(fetches).toBe(1);
- expect(a.headers.get(GITHUB_RESPONSE_CACHE_REPLAY_HEADER)).toBeNull();
- expect(b.headers.get(GITHUB_RESPONSE_CACHE_REPLAY_HEADER)).toBe("coalesced");
+ try {
+ const [a, b] = await Promise.all([
+ fetchCachedGitHubGraphQl(TOTALS_QUERY, "token-a"),
+ fetchCachedGitHubGraphQl(TOTALS_QUERY, "token-a"),
+ ]);
+
+ expect(fetches).toBe(1);
+ expect(a.headers.get(GITHUB_RESPONSE_CACHE_REPLAY_HEADER)).toBeNull();
+ expect(b.headers.get(GITHUB_RESPONSE_CACHE_REPLAY_HEADER)).toBe("coalesced");
+ } finally {
+ digestSpy.mockRestore();
+ }
});
it("does not coalesce concurrent cold misses when admission keys differ", async () => {
diff --git a/test/unit/linked-issue-satisfaction-run.test.ts b/test/unit/linked-issue-satisfaction-run.test.ts
index 8e48e52b7c..efc71739de 100644
--- a/test/unit/linked-issue-satisfaction-run.test.ts
+++ b/test/unit/linked-issue-satisfaction-run.test.ts
@@ -782,7 +782,6 @@ describe("linked-issue satisfaction wired end-to-end through the real webhook pi
// feature populates) is itself behind BOTH the global kill-switch AND the (back-compat, manifest-absent)
// LOOPOVER_REVIEW_REPOS allowlist -- see convergedFeatureActive/resolveConvergedFeature
// (src/review/feature-activation.ts). Both are required for a repo with no `.loopover.yml` manifest.
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "true",
LOOPOVER_REVIEW_REPOS: "JSONbored/metagraphed",
});
await persistRegistrySnapshot(
@@ -873,7 +872,6 @@ describe("linked-issue satisfaction wired end-to-end through the real webhook pi
AI_SUMMARIES_ENABLED: "true",
AI_PUBLIC_COMMENTS_ENABLED: "true",
AI_DAILY_NEURON_BUDGET: "100000",
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "true",
LOOPOVER_REVIEW_REPOS: "JSONbored/metagraphed",
});
await persistRegistrySnapshot(
diff --git a/test/unit/queue-3.test.ts b/test/unit/queue-3.test.ts
index 5bd6cfb81c..8f3660a93a 100644
--- a/test/unit/queue-3.test.ts
+++ b/test/unit/queue-3.test.ts
@@ -1369,7 +1369,6 @@ describe("queue processors", () => {
it("screenshot-table gate (#4110): a persisted bot capture from an earlier pass satisfies the gate, no body table needed", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_SCREENSHOTS: "true",
});
await upsertInstallation(env, {
@@ -1475,7 +1474,6 @@ describe("queue processors", () => {
const markSpy = vi.spyOn(repositoriesModule, "markPullRequestVisualCaptureSatisfied").mockRejectedValueOnce(new Error("D1 write failed"));
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_SCREENSHOTS: "true",
});
await upsertInstallation(env, {
diff --git a/test/unit/queue-4.test.ts b/test/unit/queue-4.test.ts
index a1a913a441..b950358f52 100644
--- a/test/unit/queue-4.test.ts
+++ b/test/unit/queue-4.test.ts
@@ -2720,10 +2720,9 @@ describe("queue processors", () => {
// #1007 convergence (Stage D) / #6103: the public PR-panel comment is rendered by the UNIFIED renderer
// (GitHub alert + synthesized "Code review" row / Decision drivers) unconditionally now -- leading with
- // the same panel marker so the in-place upsert updates the same comment. `LOOPOVER_REVIEW_UNIFIED_COMMENT`
- // below is kept for historical parity with sibling tests; it's inert (no longer read for this decision).
+ // the same panel marker so the in-place upsert updates the same comment.
it("renders the unified PR-review comment when the gate evaluates", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -2909,7 +2908,6 @@ describe("queue processors", () => {
it("#4744: threads the improvement-signal row into the unified comment when the converged feature resolves on", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_IMPROVEMENT_SIGNAL: "true",
LOOPOVER_REVIEW_REPOS: "JSONbored/gittensory",
});
@@ -3088,7 +3086,6 @@ describe("queue processors", () => {
it("#4745: threads the real slop band into the Improvement row's quadrant prefix when both improvementSignal and slop evidence collection are on", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_IMPROVEMENT_SIGNAL: "true",
LOOPOVER_REVIEW_REPOS: "JSONbored/gittensory",
});
@@ -3259,7 +3256,7 @@ describe("queue processors", () => {
});
it("INVARIANT (#4498): the disposition planner reuses the public surface's own live mergeable_state/CI read instead of re-fetching a third time", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -3381,7 +3378,6 @@ describe("queue processors", () => {
it("threads review.visual config into the capture pipeline and renders a Visual preview section (#3609 / #3610)", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_SCREENSHOTS: "true",
});
await persistRegistrySnapshot(
@@ -3529,7 +3525,6 @@ describe("queue processors", () => {
it("skips the capture pipeline entirely when review.visual.enabled is false, even though the env-var gate allows it (#4083)", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_SCREENSHOTS: "true",
});
await persistRegistrySnapshot(
@@ -3673,7 +3668,7 @@ describe("queue processors", () => {
// unified branch already does for the readiness chip — no separate call, no AI. Mirrors the base unified-comment
// test above but adds the manifest opt-in and asserts the new section's presence + content.
it("renders the Changed files summary when review.changed_files_summary is on in .loopover.yml", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -3846,7 +3841,7 @@ describe("queue processors", () => {
// from the SAME PR-files fetch the unified branch already does (no separate call). Mirrors the
// changed_files_summary test above but asserts the effort chip's presence + exact value instead.
it("renders the review effort chip when review.effort_score is on in .loopover.yml", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -4028,7 +4023,7 @@ describe("queue processors", () => {
// for the readiness chip and gate verdict, no extra fetch. Mirrors the effort_score test above but asserts
// the auto-merge-readiness table's presence + condition marks instead.
it("renders the Auto-merge readiness collapsible when review.auto_merge_summary is on in .loopover.yml", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -4372,7 +4367,7 @@ describe("queue processors", () => {
// manifest caps into `buildUnifiedCommentBody` and the renderer truncates blocker/nit lists with a "+N more"
// footer. Mirrors the effort_score test above but asserts display-only truncation instead.
it("truncates unified-comment blockers when review.max_findings is set in .loopover.yml (#2049)", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -4692,7 +4687,7 @@ describe("queue processors", () => {
}
it("FLAG-OFF (default): review.memory in .loopover.yml alone never suppresses the readiness warning (operator kill-switch required)", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
// review.memory: true in the manifest, but NO LOOPOVER_REVIEW_MEMORY env flag on this env -- byte-identical.
const postedBody = await runReadinessWarningPass(env, {
deliveryId: "review-memory-flag-off",
@@ -4703,7 +4698,7 @@ describe("queue processors", () => {
});
it("FLAG-ON: suppresses a readiness warning EXACTLY matching a previously recorded suppression signal", async () => {
- const seedEnv = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const seedEnv = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
// A throwaway pass (flag/manifest both off — byte-identical review path) against a SEPARATE, disposable D1
// instance just to learn the finding's REAL, LIVE-computed readiness score (a pure function of the fixed
// PR/settings fixture above, so it reproduces identically for the real pass below on its own fresh `env`).
@@ -4717,7 +4712,7 @@ describe("queue processors", () => {
// Reconstructs buildQualityGateWarning's exact title+detail template (src/rules/advisory.ts) from the live
// score + the qualityGateMinScore: 100 configured above, so the computed patternHash matches the real finding.
const detail = `The public readiness score is ${score}/100, below the repository threshold of 100/100.`;
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1", LOOPOVER_REVIEW_MEMORY: "true" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_MEMORY: "true" });
await recordReviewSuppression(env, {
repoFullName: "JSONbored/gittensory",
category: "readiness_score_below_threshold",
@@ -4738,7 +4733,7 @@ describe("queue processors", () => {
});
it("FLAG-ON, no stored signals: neither suppresses nor demotes -- the warning renders exactly as if review.memory were off (REGRESSION: the all-clear branch where the store read succeeds but finds nothing to apply)", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1", LOOPOVER_REVIEW_MEMORY: "true" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_MEMORY: "true" });
// Flag is fully ON (env + manifest) and the suppression-store read succeeds, but NO signal has ever been
// recorded for this repo -- applyReviewMemorySuppression's own empty-signals short-circuit returns
// suppressedCount: 0, demotedCount: 0, so processors.ts's "anything to apply?" check is false and
@@ -4752,7 +4747,7 @@ describe("queue processors", () => {
});
it("FLAG-ON: DEMOTES (keeps, but does not suppress) a same-category readiness warning that does not exactly match any stored signal", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1", LOOPOVER_REVIEW_MEMORY: "true" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_MEMORY: "true" });
// A signal for the SAME category but a patternHash that can never match this PR's real finding -- exercises
// the "demote" (scope-matched, hash-mismatched) branch instead of "suppress".
await recordReviewSuppression(env, {
@@ -4771,7 +4766,7 @@ describe("queue processors", () => {
});
it("FLAG-ON, fail-safe: a suppression-store read error leaves the readiness warning untouched rather than throwing", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_UNIFIED_COMMENT: "1", LOOPOVER_REVIEW_MEMORY: "true" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), LOOPOVER_REVIEW_MEMORY: "true" });
const listSpy = vi.spyOn(repositoriesModule, "listReviewSuppressions").mockRejectedValue(new Error("D1 unavailable"));
try {
const postedBody = await runReadinessWarningPass(env, {
@@ -4852,7 +4847,6 @@ describe("queue processors", () => {
it("renders finding categories in the inline comment label and the unified comment's Finding categories section when review.finding_categories is on", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_INLINE_COMMENTS: "true",
LOOPOVER_REVIEW_REPOS: "JSONbored/gittensory",
AI: {
@@ -4944,7 +4938,6 @@ describe("queue processors", () => {
let aiCalls = 0;
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
AI: { run: async () => { aiCalls += 1; return { response: JSON.stringify({ assessment: "Fresh.", blockers: [], nits: [], suggestions: [] }) }; } } as unknown as Ai,
AI_SUMMARIES_ENABLED: "true",
AI_PUBLIC_COMMENTS_ENABLED: "true",
@@ -4989,7 +4982,6 @@ describe("queue processors", () => {
it("emits the Fix handoff collapsible in the unified comment when review.fixHandoff + the operator flag are on", async () => {
const env = createTestEnv({
GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(),
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "1",
LOOPOVER_REVIEW_INLINE_COMMENTS: "true",
LOOPOVER_REVIEW_FIX_HANDOFF: "true",
LOOPOVER_REVIEW_REPOS: "JSONbored/gittensory",
@@ -5064,7 +5056,7 @@ describe("queue processors", () => {
// real diff/changed-file count on the first review, and (D3) the failing check name + its per-check WHY render
// under a "CI checks failing" section (not just a bare "CI failing" chip).
it("inline-fetches the PR files and renders failing CI check names + reasons in the unified comment (FIX B + D3)", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITHUB_PUBLIC_TOKEN: "public-token", LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITHUB_PUBLIC_TOKEN: "public-token" });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -5221,7 +5213,7 @@ describe("queue processors", () => {
// under its own non-blocking "Flagged checks" section, so a maintainer can act on it without the PR being
// silently waved through OR silently closed.
it("REGRESSION (#4414-class advisory holds): a non-required third-party action_required check renders as a non-blocking 'Flagged checks' note, not a CI failure", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITHUB_PUBLIC_TOKEN: "public-token", LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITHUB_PUBLIC_TOKEN: "public-token" });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
@@ -5369,7 +5361,7 @@ describe("queue processors", () => {
});
it("REGRESSION (#4414-class advisory holds): a bare non-required action_required check (no output/details_url) still renders under 'Flagged checks', name-only", async () => {
- const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITHUB_PUBLIC_TOKEN: "public-token", LOOPOVER_REVIEW_UNIFIED_COMMENT: "1" });
+ const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem(), GITHUB_PUBLIC_TOKEN: "public-token" });
await persistRegistrySnapshot(
env,
normalizeRegistryPayload(
diff --git a/test/unit/queue-5.test.ts b/test/unit/queue-5.test.ts
index 5dd2554c3c..7e14f2cd87 100644
--- a/test/unit/queue-5.test.ts
+++ b/test/unit/queue-5.test.ts
@@ -5562,7 +5562,7 @@ describe("queue processors", () => {
aiReviewMode: "off",
typeLabelsEnabled: false,
});
- await upsertRepoFocusManifest(env, repoFullName, { testExpectations: ["Run npm run test:ci."], features: { e2eTests: true, unifiedComment: true } });
+ await upsertRepoFocusManifest(env, repoFullName, { testExpectations: ["Run npm run test:ci."], features: { e2eTests: true } });
// gateEvaluation needs a resolved CI aggregate (mocking the module function directly is far simpler than
// stubbing every raw status/check-suite endpoint the live CI aggregator would otherwise call) -- but
// NOT "passed": resolveManifestPassedValidationCount treats a fully-green live CI rollup as validation
diff --git a/test/unit/reputation-wiring.test.ts b/test/unit/reputation-wiring.test.ts
index 4a10e2cd8b..20ceea7355 100644
--- a/test/unit/reputation-wiring.test.ts
+++ b/test/unit/reputation-wiring.test.ts
@@ -472,8 +472,7 @@ describe("processGitHubWebhook records the reputation outcome on a terminal PR (
it("FLAG-ON: a closed+merged PR webhook records a 'merged' outcome for the submitter", async () => {
const { processJob } = await import("../../src/queue/processors");
const { upsertRepositorySettings } = await import("../../src/db/repositories");
- // LOOPOVER_REVIEW_UNIFIED_COMMENT on so the closing-PR comment path takes the unified-renderer branch.
- const env = createTestEnv({ LOOPOVER_REVIEW_REPUTATION: "true", LOOPOVER_REVIEW_UNIFIED_COMMENT: "true" });
+ const env = createTestEnv({ LOOPOVER_REVIEW_REPUTATION: "true" });
// Gate enabled so the closing-PR public-surface path (skipped-gate + unified closed comment) executes.
await upsertRepositorySettings(env, { repoFullName: "JSONbored/gittensory" });
// External calls (token/miner/github) are best-effort + caught; stub them so nothing throws.
diff --git a/test/unit/unified-comment-bridge.test.ts b/test/unit/unified-comment-bridge.test.ts
index 8f7d0cd0c9..e5170b7b84 100644
--- a/test/unit/unified-comment-bridge.test.ts
+++ b/test/unit/unified-comment-bridge.test.ts
@@ -6,7 +6,6 @@ import {
buildVisualFindingsCollapsible,
consensusDefectFromFindings,
gateConclusionToVerdict,
- isUnifiedReviewCommentEnabled,
isBoilerplateNit,
panelRowsToSignalRows,
PR_PANEL_COMMENT_MARKER,
@@ -1308,25 +1307,6 @@ describe("buildClosedUnifiedCommentBody (closed/skipped PR through the unified r
// queue/GitHub-client harness. The focused unit coverage here (open + closed body, marker single-source, flag
// gate, Nit scrub) asserts the bridge contract the processor relies on; the e2e wiring is a separate task.
-// #6103: the flag this function reads no longer selects between two comment renderers (the legacy
-// buildPublicPrIntelligenceComment path it used to gate was deleted, having no remaining production
-// caller) -- kept functionally inert so an operator's existing deployment config setting it doesn't error.
-describe("isUnifiedReviewCommentEnabled (pure flag parsing, now inert)", () => {
- it("parses OFF when the flag is unset or falsy", () => {
- expect(isUnifiedReviewCommentEnabled({})).toBe(false);
- expect(isUnifiedReviewCommentEnabled({ LOOPOVER_REVIEW_UNIFIED_COMMENT: undefined })).toBe(false);
- expect(isUnifiedReviewCommentEnabled({ LOOPOVER_REVIEW_UNIFIED_COMMENT: "false" })).toBe(false);
- expect(isUnifiedReviewCommentEnabled({ LOOPOVER_REVIEW_UNIFIED_COMMENT: "0" })).toBe(false);
- expect(isUnifiedReviewCommentEnabled({ LOOPOVER_REVIEW_UNIFIED_COMMENT: "" })).toBe(false);
- });
-
- it("is ON only for an explicit truthy value", () => {
- for (const value of ["1", "true", "yes", "on", "TRUE", "On"]) {
- expect(isUnifiedReviewCommentEnabled({ LOOPOVER_REVIEW_UNIFIED_COMMENT: value })).toBe(true);
- }
- });
-});
-
describe("comment size-budget guard (#6069)", () => {
it("leaves a normal-sized comment completely untouched (no trimming, no note)", () => {
const body = buildUnifiedCommentBody({
diff --git a/test/unit/visual-wire.test.ts b/test/unit/visual-wire.test.ts
index 6136468265..82f44e214b 100644
--- a/test/unit/visual-wire.test.ts
+++ b/test/unit/visual-wire.test.ts
@@ -26,7 +26,7 @@ describe("isScreenshotsEnabled", () => {
describe("screenshots converged-feature activation (env flag AND repo cutover allowlist, with manifest opt-out)", () => {
const repo = "JSONbored/gittensory";
const noOverride: Pick = {
- features: { present: false, rag: null, reputation: null, unifiedComment: null, safety: null, grounding: null, e2eTests: null, screenshots: null, improvementSignal: null },
+ features: { present: false, rag: null, reputation: null, safety: null, grounding: null, e2eTests: null, screenshots: null, improvementSignal: null },
};
it("requires BOTH the global flag and the repo allowlist when no override is set", () => {
diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts
index b52c809f16..70fbc2c205 100644
--- a/worker-configuration.d.ts
+++ b/worker-configuration.d.ts
@@ -1,5 +1,5 @@
/* eslint-disable */
-// Generated by Wrangler by running `wrangler types` (hash: 4d6b19cd1b49c2d246210b2839cf1394)
+// Generated by Wrangler by running `wrangler types` (hash: 959cc16be651caa7cf26367a5a83821e)
// Runtime types generated with workerd@1.20260701.1 2026-05-28 nodejs_compat
interface __BaseEnv_Env {
REVIEW_AUDIT: R2Bucket;
@@ -18,7 +18,6 @@ interface __BaseEnv_Env {
PUBLIC_SITE_ORIGIN: "https://loopover.ai";
PUBLIC_SITE_ORIGIN_ALIASES: "https://gittensory.aethereal.dev";
ADMIN_GITHUB_LOGINS: "JSONbored";
- LOOPOVER_REVIEW_UNIFIED_COMMENT: "false";
LOOPOVER_REVIEW_INLINE_COMMENTS: "false";
LOOPOVER_REVIEW_SAFETY: "false";
LOOPOVER_REVIEW_SCREENSHOTS: "false";
@@ -99,7 +98,6 @@ declare namespace NodeJS {
| "LOOPOVER_REVIEW_SAFETY"
| "LOOPOVER_REVIEW_SCREENSHOTS"
| "LOOPOVER_REVIEW_SELFTUNE"
- | "LOOPOVER_REVIEW_UNIFIED_COMMENT"
| "LOOPOVER_SKIP_AUTOMATION_BOT_PRS"
| "LOOPOVER_SWEEP_WATCHDOG"
| "PUBLIC_API_ORIGIN"
diff --git a/wrangler.jsonc b/wrangler.jsonc
index 0f24b51c25..0de2abfff6 100644
--- a/wrangler.jsonc
+++ b/wrangler.jsonc
@@ -56,7 +56,6 @@
// Hosted reviews are retired. Cloudflare now serves the public API + Orb broker only; review execution runs
// in the self-host container, where Redis-backed transient state is mandatory. The Cloudflare API worker no
// longer binds Workers AI, Vectorize, R2 review audit storage, or Browser Rendering for review execution.
- "LOOPOVER_REVIEW_UNIFIED_COMMENT": "false",
// Inline comments (#inline-comments): leave quiet, non-blocking inline comments on changed lines, on top of
// the decision summary. Requires the repo in LOOPOVER_REVIEW_REPOS AND review.inline_comments in its
// .loopover.yml. Default OFF — flag-OFF the model is never asked for inline findings (byte-identical).