Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 9 additions & 15 deletions src/queue/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@
buildRoleContext,
detectGittensorContributor,
PR_PANEL_RETRIGGER_MARKER,
unionScopedOverlapClusters,

Check notice on line 147 in src/queue/processors.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 147 in src/queue/processors.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Open PR queue is busy

This repo has a busy open PR queue in the local Gittensory cache.

Check notice on line 147 in src/queue/processors.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
type ContributorProfile,
} from "../signals/engine";
import { buildClosedUnifiedCommentBody, buildUnifiedCommentBody, isUnifiedReviewCommentEnabled } from "../review/unified-comment-bridge";
import { buildClosedUnifiedCommentBody, buildMergeReadinessFromChecks, buildUnifiedCommentBody, isUnifiedReviewCommentEnabled } from "../review/unified-comment-bridge";
import type { MergeReadiness } from "../review/unified-comment";
import { buildIssueSlopAssessment, buildSlopAssessment, type SlopBand } from "../signals/slop";
import { runGittensoryAiSlopAdvisory } from "../services/ai-slop";
Expand Down Expand Up @@ -1809,22 +1809,16 @@
if (unifiedCommentAllowed && gateEvaluation) {
const { rows, readinessTotal } = buildPublicPrPanelSignalRows({ repo, pr, profile, detection, queueHealth, collisions, preflight, settings, gate: gateEvaluation });
const unifiedFiles = await listPullRequestFiles(env, repoFullName, pr.number);
// CI + merge-state readiness — a converged enrichment the legacy panel never showed. Maps each cached
// check's conclusion to passed/failed/unverified; any failure (failure/timed_out/cancelled/action_required)
// flips the whole PR to 'failed'. The gate decision stays authoritative for the comment's color (always
// passed here), so these CI chips never spuriously flip the unified status to held/blocked.
// CI + merge-state readiness — a converged enrichment the legacy panel never showed. Only current-head
// check summaries can affect the chip, and pending/in-progress current-head checks remain unverified.
// The gate decision stays authoritative for the comment's color (always passed here), so these CI chips
// never spuriously flip the unified status to held/blocked.
const checkSummaries = await listCheckSummaries(env, repoFullName, pr.number);
const failedChecks = checkSummaries.filter((check) => {
const conclusion = (check.conclusion ?? "").toLowerCase();
return conclusion === "failure" || conclusion === "timed_out" || conclusion === "cancelled" || conclusion === "action_required";
const mergeReadiness: MergeReadiness = buildMergeReadinessFromChecks({
checks: checkSummaries,
headSha: pr.headSha,
mergeableState: pr.mergeableState,
});
const anyPassed = checkSummaries.some((check) => (check.conclusion ?? "").toLowerCase() === "success");
const ciState: MergeReadiness["ciState"] = failedChecks.length > 0 ? "failed" : anyPassed ? "passed" : "unverified";
const mergeReadiness: MergeReadiness = {
ciState,
...(pr.mergeableState ? { mergeStateLabel: pr.mergeableState } : {}),
...(failedChecks.length > 0 ? { failingChecks: failedChecks.map((check) => check.name) } : {}),
};
deterministicBody = buildUnifiedCommentBody({
gate: gateEvaluation,
...(aiReview !== undefined ? { aiReview } : {}),
Expand Down
33 changes: 32 additions & 1 deletion src/review/unified-comment-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
// `consensusDefectOf`); the signal rows via the panel helpers' `sanitizePanelText`. The ONE input not
// covered by an existing filter is the gate's `warnings` (rendered as Nits) — those carry an
// AdvisoryFinding's raw title/action, which the check-run path sanitizes (`sanitizeForCheckRun`) but this
// comment path historically did not. This module therefore scrubs Nits itself (see `publicSafeNit` /

Check notice on line 18 in src/review/unified-comment-bridge.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 18 in src/review/unified-comment-bridge.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Open PR queue is busy

This repo has a busy open PR queue in the local Gittensory cache.

Check notice on line 18 in src/review/unified-comment-bridge.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
// `PRIVATE_FORBIDDEN_TERMS`) as defense-in-depth before they reach a public comment.

import type { AdvisoryFinding } from "../types";
import type { AdvisoryFinding, CheckSummaryRecord } from "../types";
import type { GateCheckConclusion, GateCheckEvaluation } from "../rules/advisory";
import type { PublicPrPanelSignalRow } from "../signals/engine";
// Single-source the panel marker from its canonical home (the upsert reads it there); re-export so existing
Expand Down Expand Up @@ -56,6 +56,37 @@
// Mirrors src/rules/advisory.ts CHECK_RUN_FORBIDDEN_TERMS (scrubbed → "[context]") and
// src/signals/engine.ts containsPrivatePublicTerm (drop if still present). Kept inline so this module
// stays a pure, dependency-light renderer-mapping seam.

const FAILED_CHECK_CONCLUSIONS = new Set(["failure", "timed_out", "cancelled", "action_required"]);

function isCompletedSuccessfulCheck(check: CheckSummaryRecord): boolean {
return check.status.toLowerCase() === "completed" && (check.conclusion ?? "").toLowerCase() === "success";
}

/**
* Build public merge-readiness from cached check summaries without allowing stale or partial CI to look green.
* Only check summaries for the current PR head SHA are considered, and CI is green only when every current-head
* check is completed successfully. Pending/in-progress checks, missing checks, or an unknown head SHA are
* deliberately rendered as unverified.
*/
export function buildMergeReadinessFromChecks(input: {
checks: readonly CheckSummaryRecord[];
headSha?: string | null | undefined;
mergeableState?: string | null | undefined;
}): MergeReadiness {
const currentHead = input.headSha?.trim();
const currentChecks = currentHead ? input.checks.filter((check) => check.headSha === currentHead) : [];
const failedChecks = currentChecks.filter((check) => FAILED_CHECK_CONCLUSIONS.has((check.conclusion ?? "").toLowerCase()));
const ciState: MergeReadiness["ciState"] =
failedChecks.length > 0 ? "failed" : currentChecks.length > 0 && currentChecks.every(isCompletedSuccessfulCheck) ? "passed" : "unverified";

return {
ciState,
...(input.mergeableState ? { mergeStateLabel: input.mergeableState } : {}),
...(failedChecks.length > 0 ? { failingChecks: failedChecks.map((check) => check.name) } : {}),
};
}

const PRIVATE_FORBIDDEN_TERMS =
/\b(?:rewards?|payouts?|farming|estimated\s+scores?|raw\s+trust\s+scores?|trust\s+scores?|score\s+estimates?|reward\s+estimates?|wallets?|hotkeys?|coldkeys?|reviewability|scoreability|private\s+signals?|likely_duplicate|reviewability\s*\d)\b/gi;
const PRIVATE_DROP_TERMS = /\b(?:reward|payout|farming|wallet|hotkey|trust score|raw trust|estimated score|scoreability|likely_duplicate|reviewability\s*\d)\b/i;
Expand Down
45 changes: 44 additions & 1 deletion test/unit/unified-comment-bridge.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { describe, expect, it } from "vitest";
import {

Check notice on line 2 in test/unit/unified-comment-bridge.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 2 in test/unit/unified-comment-bridge.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Open PR queue is busy

This repo has a busy open PR queue in the local Gittensory cache.

Check notice on line 2 in test/unit/unified-comment-bridge.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
buildClosedUnifiedCommentBody,
buildDualReviewNotes,
buildMergeReadinessFromChecks,
buildUnifiedCommentBody,
consensusDefectFromFindings,
gateConclusionToVerdict,
Expand All @@ -13,7 +14,7 @@
import { PR_PANEL_COMMENT_MARKER as MARKER_FROM_COMMENTS } from "../../src/github/comments";
import { deriveUnifiedStatus, type MergeReadiness, type UnifiedCollapsible, type UnifiedCommentStatus } from "../../src/review/unified-comment";
import type { GateCheckEvaluation } from "../../src/rules/advisory";
import type { AdvisoryFinding } from "../../src/types";
import type { AdvisoryFinding, CheckSummaryRecord } from "../../src/types";
import type { PublicPrPanelSignalRow } from "../../src/signals/engine";

function gate(over: Partial<GateCheckEvaluation> = {}): GateCheckEvaluation {
Expand Down Expand Up @@ -42,6 +43,48 @@

const footer = "💰 **Earn for open-source contributions like this.** Checked by Gittensory.";

describe("buildMergeReadinessFromChecks", () => {
const check = (over: Partial<CheckSummaryRecord> = {}): CheckSummaryRecord => ({
id: over.id ?? `${over.headSha ?? "head"}-${over.name ?? "test"}`,
repoFullName: "JSONbored/gittensory",
pullNumber: 42,
headSha: "current-head",
name: "test",
status: "completed",
conclusion: "success",
payload: {},
...over,
});

it("does not report stale or partial cached CI as green", () => {
expect(
buildMergeReadinessFromChecks({
headSha: "current-head",
checks: [check({ headSha: "old-head", conclusion: "success" }), check({ name: "build", status: "in_progress", conclusion: null })],
}),
).toEqual({ ciState: "unverified" });
});

it("requires every current-head check to be completed successfully before showing CI green", () => {
expect(
buildMergeReadinessFromChecks({
headSha: "current-head",
mergeableState: "clean",
checks: [check({ name: "test" }), check({ name: "lint" })],
}),
).toEqual({ ciState: "passed", mergeStateLabel: "clean" });
});

it("reports current-head failures with failing check names", () => {
expect(
buildMergeReadinessFromChecks({
headSha: "current-head",
checks: [check({ name: "test", conclusion: "failure" }), check({ name: "lint" })],
}),
).toEqual({ ciState: "failed", failingChecks: ["test"] });
});
});

describe("gateConclusionToVerdict", () => {
it("maps every gate conclusion to its authoritative verdict", () => {
expect(gateConclusionToVerdict("success")).toBe("merge");
Expand Down