mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:00:44 +00:00
Require real behavior proof for external PRs (#77622)
* ci: require real behavior proof for external PRs * fix: tighten real behavior proof heuristics * fix: reject test-only real behavior proof labels --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
// Barnacle owns deterministic GitHub triage and auto-response behavior.
|
||||
|
||||
import {
|
||||
MOCK_ONLY_PROOF_LABEL,
|
||||
NEEDS_REAL_BEHAVIOR_PROOF_LABEL,
|
||||
PROOF_OVERRIDE_LABEL,
|
||||
evaluateRealBehaviorProof,
|
||||
labelsForRealBehaviorProof,
|
||||
} from "./real-behavior-proof-policy.mjs";
|
||||
|
||||
const activePrLimit = 20;
|
||||
|
||||
const thirdPartyExtensionMessage =
|
||||
@@ -134,6 +142,18 @@ export const managedLabelSpecs = {
|
||||
color: "C5DEF5",
|
||||
description: "Candidate: PR template appears mostly untouched.",
|
||||
},
|
||||
[NEEDS_REAL_BEHAVIOR_PROOF_LABEL]: {
|
||||
color: "C5DEF5",
|
||||
description: "Candidate: external PR needs after-fix proof from a real setup.",
|
||||
},
|
||||
[MOCK_ONLY_PROOF_LABEL]: {
|
||||
color: "C5DEF5",
|
||||
description: "Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI.",
|
||||
},
|
||||
[PROOF_OVERRIDE_LABEL]: {
|
||||
color: "C2E0C6",
|
||||
description: "Maintainer override for the external PR real behavior proof gate.",
|
||||
},
|
||||
"triage: dirty-candidate": {
|
||||
color: "C5DEF5",
|
||||
description: "Candidate: broad unrelated surfaces; may need splitting or cleanup.",
|
||||
@@ -154,6 +174,8 @@ export const candidateLabels = {
|
||||
docsDiscoverability: "triage: docs-discoverability",
|
||||
testOnlyNoBug: "triage: test-only-no-bug",
|
||||
refactorOnly: "triage: refactor-only",
|
||||
needsRealBehaviorProof: NEEDS_REAL_BEHAVIOR_PROOF_LABEL,
|
||||
mockOnlyProof: MOCK_ONLY_PROOF_LABEL,
|
||||
dirtyCandidate: "triage: dirty-candidate",
|
||||
riskyInfra: "triage: risky-infra",
|
||||
externalPluginCandidate: "triage: external-plugin-candidate",
|
||||
@@ -196,10 +218,23 @@ const maintainerAuthorLabel = "maintainer";
|
||||
const privilegedAuthorAssociations = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
|
||||
const privilegedRepositoryRoles = new Set(["admin", "maintain", "write"]);
|
||||
const candidateLabelValues = Object.values(candidateLabels);
|
||||
const proofCandidateLabelValues = [NEEDS_REAL_BEHAVIOR_PROOF_LABEL, MOCK_ONLY_PROOF_LABEL];
|
||||
const noisyPrMessage =
|
||||
"Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.";
|
||||
|
||||
const candidateActionRules = [
|
||||
{
|
||||
label: candidateLabels.needsRealBehaviorProof,
|
||||
close: true,
|
||||
message:
|
||||
"Closing this PR because it does not include real behavior proof. Please reopen or resubmit with after-fix evidence from a real OpenClaw setup; terminal screenshots, console output, redacted logs, recordings, linked artifacts, and copied live output count. Unit tests, mocks, snapshots, lint, typechecks, and CI are supplemental only.",
|
||||
},
|
||||
{
|
||||
label: candidateLabels.mockOnlyProof,
|
||||
close: true,
|
||||
message:
|
||||
"Closing this PR because the proof only shows tests, mocks, snapshots, lint, typechecks, or CI. Please reopen or resubmit with after-fix evidence from a real OpenClaw setup; terminal screenshots, console output, redacted logs, recordings, linked artifacts, and copied live output count.",
|
||||
},
|
||||
{
|
||||
label: candidateLabels.dirtyCandidate,
|
||||
close: true,
|
||||
@@ -438,6 +473,14 @@ export function classifyPullRequestCandidateLabels(pullRequest, files) {
|
||||
labelsToAdd.push(candidateLabels.blankTemplate);
|
||||
}
|
||||
|
||||
labelsToAdd.push(
|
||||
...labelsForRealBehaviorProof(
|
||||
evaluateRealBehaviorProof({
|
||||
pullRequest,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const docsOnly = filenames.every(isMarkdownOrDocsFile);
|
||||
const docsSignal =
|
||||
/\b(add|adds|update|updates|fix|fixes|improve|cleanup|clean up|typo|readme|docs?|documentation|translation|translate)\b/i.test(
|
||||
@@ -718,14 +761,18 @@ async function addMissingLabels(github, context, core, issueNumber, labels, labe
|
||||
|
||||
async function applyPullRequestCandidateLabels(github, context, core, pullRequest, labelSet) {
|
||||
const files = await listPullRequestFiles(github, context, pullRequest);
|
||||
await addMissingLabels(
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
pullRequest.number,
|
||||
classifyPullRequestCandidateLabels(pullRequest, files),
|
||||
labelSet,
|
||||
const classifiedLabels = classifyPullRequestCandidateLabels(
|
||||
{
|
||||
...pullRequest,
|
||||
labels: [...labelSet].map((name) => ({ name })),
|
||||
},
|
||||
files,
|
||||
);
|
||||
const staleProofLabels = proofCandidateLabelValues.filter(
|
||||
(label) => labelSet.has(label) && !classifiedLabels.includes(label),
|
||||
);
|
||||
await removeLabels(github, context, pullRequest.number, staleProofLabels, labelSet);
|
||||
await addMissingLabels(github, context, core, pullRequest.number, classifiedLabels, labelSet);
|
||||
}
|
||||
|
||||
function isAutomationUser(user, fallbackLogin = "") {
|
||||
@@ -931,7 +978,9 @@ export async function runBarnacleAutoResponse({ github, context, core = console
|
||||
const isLabelEvent = context.payload.action === "labeled";
|
||||
const isPrCandidateEvent =
|
||||
pullRequest &&
|
||||
["opened", "edited", "synchronize", "reopened", "labeled"].includes(context.payload.action);
|
||||
["opened", "edited", "synchronize", "reopened", "labeled", "unlabeled"].includes(
|
||||
context.payload.action,
|
||||
);
|
||||
if (!hasTriggerLabel && !isLabelEvent && !isPrCandidateEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user