fix: preserve Google Gemini 3 cron thinking (#85300)

Summary:
- The branch adds a Google provider thinking-policy resolver and opt-in profile flag, updates shared thinking validation and cron/proof-policy tests, and adjusts ClawSweeper proof parsing.
- Reproducibility: yes. source-reproducible: current main applies the generic off-only profile before provider ... figured thinking through that resolver. I did not execute a live systemd cron run in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: preserve Google Gemini 3 cron thinking

Validation:
- ClawSweeper review passed for head a6cd2e826e.
- Required merge gates passed before the squash merge.

Prepared head SHA: a6cd2e826e
Review: https://github.com/openclaw/openclaw/pull/85300#issuecomment-4517662575

Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
This commit is contained in:
clawsweeper[bot]
2026-05-22 11:21:57 +00:00
committed by GitHub
parent 85e468d275
commit 77a1b7625d
14 changed files with 346 additions and 34 deletions

View File

@@ -6,6 +6,7 @@ export const MOCK_ONLY_PROOF_LABEL = "triage: mock-only-proof";
export const MAINTAINER_TEAM_SLUG = "maintainer";
export const CLAWSWEEPER_PROOF_VERDICT_STATUS = "clawsweeper_exact_head_pass";
const CLAWSWEEPER_BOT_LOGINS = new Set(["clawsweeper[bot]", "openclaw-clawsweeper[bot]"]);
const privilegedAuthorAssociations = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
@@ -142,11 +143,10 @@ export async function isMaintainerTeamMember({
return body?.state === "active";
}
export function extractRealBehaviorProofSection(body = "") {
function extractMarkdownSection(headingRegex, body = "") {
// Normalize CRLF → LF so regexes and section slicing see GitHub web-editor PR
// bodies the same way as locally-authored Markdown.
const normalizedBody = normalizeLineEndings(body);
const headingRegex = /^#{2,6}\s+real behavior proof\b[^\n]*$/gim;
const match = headingRegex.exec(normalizedBody);
if (!match) {
return "";
@@ -157,6 +157,14 @@ export function extractRealBehaviorProofSection(body = "") {
return (nextHeading ? rest.slice(0, nextHeading.index) : rest).trim();
}
export function extractRealBehaviorProofSection(body = "") {
return extractMarkdownSection(/^#{2,6}\s+real behavior proof\b[^\n]*$/im, body);
}
function extractOutOfScopeFollowUpsSection(body = "") {
return extractMarkdownSection(/^#{2,6}\s+out-of-scope follow-ups\b[^\n]*$/im, body);
}
function fieldLineRegex(name) {
return new RegExp(
`^\\s*(?:[-*]\\s*)?(?:\\*\\*)?${escapeRegex(name)}(?:\\s*\\([^)]*\\))?(?:\\*\\*)?\\s*:\\s*(.*)$`,
@@ -246,7 +254,14 @@ function isTrustedClawSweeperComment(comment) {
const appSlug = String(
comment?.performed_via_github_app?.slug ?? comment?.performedViaGithubApp?.slug ?? "",
).toLowerCase();
return appSlug === "clawsweeper";
if (appSlug === "clawsweeper") {
return true;
}
// GitHub can omit performed_via_github_app on issue comments while still
// returning a reserved ClawSweeper App bot identity.
const login = String(comment?.user?.login ?? "").toLowerCase();
const userType = String(comment?.user?.type ?? "");
return CLAWSWEEPER_BOT_LOGINS.has(login) && userType === "Bot";
}
export function hasClawSweeperExactHeadProof({ pullRequest, comments = [] } = {}) {
@@ -292,7 +307,8 @@ export function evaluateRealBehaviorProof({ pullRequest, labels } = {}) {
return result("skipped", "Maintainer, collaborator, or bot PRs do not require this gate.");
}
const section = extractRealBehaviorProofSection(pullRequest?.body ?? "");
const body = pullRequest?.body ?? "";
const section = extractRealBehaviorProofSection(body);
if (!section) {
return result(
"missing",
@@ -303,6 +319,9 @@ export function evaluateRealBehaviorProof({ pullRequest, labels } = {}) {
const fields = Object.fromEntries(
requiredProofFields.map((field) => [field.key, extractFieldValue(section, field)]),
);
if (!fields.notTested) {
fields.notTested = extractOutOfScopeFollowUpsSection(body);
}
const missingFields = requiredProofFields
.filter((field) => isMissingValue(fields[field.key] ?? "", field))
.map((field) => field.key);