@attalabs/vinaya 0.12.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -2
- package/aeg-root/contracts/planner-brief.md +1 -1
- package/aeg-root/enforcement.md +5 -5
- package/aeg-root/roles/planner.md +2 -2
- package/aeg-root/roles/reviewer.md +17 -4
- package/aeg-root/roles/security.md +16 -5
- package/aeg-root/skills/aeg/SKILL.md +1 -1
- package/aeg-root/state-machine.md +2 -2
- package/aeg-root/tranche-model.md +1 -1
- package/dist/checks/bin/check-branch-topology.js +28 -5
- package/dist/checks/bin/check-brief-shape.js +28 -5
- package/dist/checks/bin/check-closes-n.js +28 -5
- package/dist/checks/bin/check-coherence.js +30 -6
- package/dist/checks/bin/check-dead-branch-push.js +28 -5
- package/dist/checks/bin/check-dispatch-readiness.js +30 -6
- package/dist/checks/bin/check-doc-coverage-push.js +30 -6
- package/dist/checks/bin/check-doc-coverage.js +30 -6
- package/dist/checks/bin/check-evidence-fresh.js +28 -5
- package/dist/checks/bin/check-first-push-dispatch.js +30 -6
- package/dist/checks/bin/check-issue-assignment.js +28 -5
- package/dist/checks/bin/check-no-disk-state.js +28 -5
- package/dist/checks/bin/check-reader-resolvable-prose.js +28 -5
- package/dist/checks/bin/check-registry-gates.js +28 -5
- package/dist/checks/bin/check-review-gate.js +33 -8
- package/dist/checks/bin/check-single-plan-pr.js +28 -5
- package/dist/checks/bin/check-test-plan.js +28 -5
- package/dist/index.js +777 -73
- package/package.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__0e95dv5._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/1q96_modules_@clerk_nextjs_dist_esm_app-router_client_keyless-creator-reader_0lom2js.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__029-1ch._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0q56ohl._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0zfjc2o._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1_ojzp1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1d_8nm4._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1wsl6ro._.js → [root-of-the-server]__1qpl4m_._.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1zkwtx8._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__210ehg9._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_00yjcv1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0o54suy._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0rix02i._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_18-d2be._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1gw6vc9._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2ieu4oi-pp_bz.js → 0ek6qlqhokys0.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1khy4oilrab6g.js → 0fm4p9os2lsom.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1eo32v1tbjy7c.js → 0ywkq74falx-x.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1t7yy_s34022p.js → 1s9ixp5nhnevp.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/27sqffqm2wbxx.css +1 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/0qu0xgvifbacu.css +0 -1
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{ZHVE9Y9G8XKR91g27SgVX → R7xvB2iuNFp_jxENpL7xv}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{ZHVE9Y9G8XKR91g27SgVX → R7xvB2iuNFp_jxENpL7xv}/_clientMiddlewareManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{ZHVE9Y9G8XKR91g27SgVX → R7xvB2iuNFp_jxENpL7xv}/_ssgManifest.js +0 -0
|
@@ -2365,15 +2365,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2365
2365
|
return { ok: true, expectedIssue };
|
|
2366
2366
|
}
|
|
2367
2367
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2368
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2369
|
+
function extractHeadSha(comment) {
|
|
2370
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2371
|
+
return m ? m[1].toLowerCase() : null;
|
|
2372
|
+
}
|
|
2368
2373
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2369
2374
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2370
2375
|
if (clearHits.length > 0) {
|
|
2371
2376
|
const latest = clearHits[clearHits.length - 1];
|
|
2372
2377
|
const m = latest.match(valuePattern);
|
|
2373
|
-
return {
|
|
2378
|
+
return {
|
|
2379
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2380
|
+
headSha: extractHeadSha(latest),
|
|
2381
|
+
danglingNote: null
|
|
2382
|
+
};
|
|
2374
2383
|
}
|
|
2375
2384
|
return {
|
|
2376
2385
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2386
|
+
headSha: null,
|
|
2377
2387
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2378
2388
|
};
|
|
2379
2389
|
}
|
|
@@ -2387,6 +2397,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2387
2397
|
function isReviewGateExemptBranch(branch) {
|
|
2388
2398
|
return branch.startsWith("plan/");
|
|
2389
2399
|
}
|
|
2400
|
+
function isBoundToHead(extraction, headSha) {
|
|
2401
|
+
if (!extraction.headSha)
|
|
2402
|
+
return false;
|
|
2403
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2404
|
+
}
|
|
2390
2405
|
function checkReviewGate(input) {
|
|
2391
2406
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2392
2407
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2409,18 +2424,26 @@ function checkReviewGate(input) {
|
|
|
2409
2424
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2410
2425
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2411
2426
|
const securityClean = security.value === "PASS";
|
|
2412
|
-
|
|
2427
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2428
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2429
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2413
2430
|
return {
|
|
2414
2431
|
verdict: "pass",
|
|
2415
|
-
reason:
|
|
2432
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2416
2433
|
waived: false
|
|
2417
2434
|
};
|
|
2418
2435
|
}
|
|
2419
2436
|
const problems = [];
|
|
2420
|
-
if (!codeReviewClean)
|
|
2437
|
+
if (!codeReviewClean) {
|
|
2421
2438
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2422
|
-
if (!
|
|
2439
|
+
} else if (!codeReviewBound) {
|
|
2440
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2441
|
+
}
|
|
2442
|
+
if (!securityClean) {
|
|
2423
2443
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2444
|
+
} else if (!securityBound) {
|
|
2445
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2446
|
+
}
|
|
2424
2447
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2425
2448
|
return {
|
|
2426
2449
|
verdict: "fail",
|
|
@@ -2368,15 +2368,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2368
2368
|
return { ok: true, expectedIssue };
|
|
2369
2369
|
}
|
|
2370
2370
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2371
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2372
|
+
function extractHeadSha(comment) {
|
|
2373
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2374
|
+
return m ? m[1].toLowerCase() : null;
|
|
2375
|
+
}
|
|
2371
2376
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2372
2377
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2373
2378
|
if (clearHits.length > 0) {
|
|
2374
2379
|
const latest = clearHits[clearHits.length - 1];
|
|
2375
2380
|
const m = latest.match(valuePattern);
|
|
2376
|
-
return {
|
|
2381
|
+
return {
|
|
2382
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2383
|
+
headSha: extractHeadSha(latest),
|
|
2384
|
+
danglingNote: null
|
|
2385
|
+
};
|
|
2377
2386
|
}
|
|
2378
2387
|
return {
|
|
2379
2388
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2389
|
+
headSha: null,
|
|
2380
2390
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2381
2391
|
};
|
|
2382
2392
|
}
|
|
@@ -2390,6 +2400,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2390
2400
|
function isReviewGateExemptBranch(branch) {
|
|
2391
2401
|
return branch.startsWith("plan/");
|
|
2392
2402
|
}
|
|
2403
|
+
function isBoundToHead(extraction, headSha) {
|
|
2404
|
+
if (!extraction.headSha)
|
|
2405
|
+
return false;
|
|
2406
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2407
|
+
}
|
|
2393
2408
|
function checkReviewGate(input) {
|
|
2394
2409
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2395
2410
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2412,18 +2427,26 @@ function checkReviewGate(input) {
|
|
|
2412
2427
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2413
2428
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2414
2429
|
const securityClean = security.value === "PASS";
|
|
2415
|
-
|
|
2430
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2431
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2432
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2416
2433
|
return {
|
|
2417
2434
|
verdict: "pass",
|
|
2418
|
-
reason:
|
|
2435
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2419
2436
|
waived: false
|
|
2420
2437
|
};
|
|
2421
2438
|
}
|
|
2422
2439
|
const problems = [];
|
|
2423
|
-
if (!codeReviewClean)
|
|
2440
|
+
if (!codeReviewClean) {
|
|
2424
2441
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2425
|
-
if (!
|
|
2442
|
+
} else if (!codeReviewBound) {
|
|
2443
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2444
|
+
}
|
|
2445
|
+
if (!securityClean) {
|
|
2426
2446
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2447
|
+
} else if (!securityBound) {
|
|
2448
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2449
|
+
}
|
|
2427
2450
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2428
2451
|
return {
|
|
2429
2452
|
verdict: "fail",
|
|
@@ -2371,15 +2371,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2371
2371
|
return { ok: true, expectedIssue };
|
|
2372
2372
|
}
|
|
2373
2373
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2374
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2375
|
+
function extractHeadSha(comment) {
|
|
2376
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2377
|
+
return m ? m[1].toLowerCase() : null;
|
|
2378
|
+
}
|
|
2374
2379
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2375
2380
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2376
2381
|
if (clearHits.length > 0) {
|
|
2377
2382
|
const latest = clearHits[clearHits.length - 1];
|
|
2378
2383
|
const m = latest.match(valuePattern);
|
|
2379
|
-
return {
|
|
2384
|
+
return {
|
|
2385
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2386
|
+
headSha: extractHeadSha(latest),
|
|
2387
|
+
danglingNote: null
|
|
2388
|
+
};
|
|
2380
2389
|
}
|
|
2381
2390
|
return {
|
|
2382
2391
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2392
|
+
headSha: null,
|
|
2383
2393
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2384
2394
|
};
|
|
2385
2395
|
}
|
|
@@ -2393,6 +2403,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2393
2403
|
function isReviewGateExemptBranch(branch) {
|
|
2394
2404
|
return branch.startsWith("plan/");
|
|
2395
2405
|
}
|
|
2406
|
+
function isBoundToHead(extraction, headSha) {
|
|
2407
|
+
if (!extraction.headSha)
|
|
2408
|
+
return false;
|
|
2409
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2410
|
+
}
|
|
2396
2411
|
function checkReviewGate(input) {
|
|
2397
2412
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2398
2413
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2415,18 +2430,26 @@ function checkReviewGate(input) {
|
|
|
2415
2430
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2416
2431
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2417
2432
|
const securityClean = security.value === "PASS";
|
|
2418
|
-
|
|
2433
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2434
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2435
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2419
2436
|
return {
|
|
2420
2437
|
verdict: "pass",
|
|
2421
|
-
reason:
|
|
2438
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2422
2439
|
waived: false
|
|
2423
2440
|
};
|
|
2424
2441
|
}
|
|
2425
2442
|
const problems = [];
|
|
2426
|
-
if (!codeReviewClean)
|
|
2443
|
+
if (!codeReviewClean) {
|
|
2427
2444
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2428
|
-
if (!
|
|
2445
|
+
} else if (!codeReviewBound) {
|
|
2446
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2447
|
+
}
|
|
2448
|
+
if (!securityClean) {
|
|
2429
2449
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2450
|
+
} else if (!securityBound) {
|
|
2451
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2452
|
+
}
|
|
2430
2453
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2431
2454
|
return {
|
|
2432
2455
|
verdict: "fail",
|
|
@@ -3150,7 +3173,8 @@ var VinayaConfigSchema = z2.object({
|
|
|
3150
3173
|
briefSchema: BriefSchemaSchema.optional(),
|
|
3151
3174
|
managed: ManagedManifestSchema.optional(),
|
|
3152
3175
|
principals: z2.array(z2.string()).min(1).optional(),
|
|
3153
|
-
ci: z2.object({ setup: z2.string().min(1) }).optional()
|
|
3176
|
+
ci: z2.object({ setup: z2.string().min(1) }).optional(),
|
|
3177
|
+
blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional()
|
|
3154
3178
|
});
|
|
3155
3179
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
3156
3180
|
var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
|
|
@@ -2368,15 +2368,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2368
2368
|
return { ok: true, expectedIssue };
|
|
2369
2369
|
}
|
|
2370
2370
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2371
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2372
|
+
function extractHeadSha(comment) {
|
|
2373
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2374
|
+
return m ? m[1].toLowerCase() : null;
|
|
2375
|
+
}
|
|
2371
2376
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2372
2377
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2373
2378
|
if (clearHits.length > 0) {
|
|
2374
2379
|
const latest = clearHits[clearHits.length - 1];
|
|
2375
2380
|
const m = latest.match(valuePattern);
|
|
2376
|
-
return {
|
|
2381
|
+
return {
|
|
2382
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2383
|
+
headSha: extractHeadSha(latest),
|
|
2384
|
+
danglingNote: null
|
|
2385
|
+
};
|
|
2377
2386
|
}
|
|
2378
2387
|
return {
|
|
2379
2388
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2389
|
+
headSha: null,
|
|
2380
2390
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2381
2391
|
};
|
|
2382
2392
|
}
|
|
@@ -2390,6 +2400,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2390
2400
|
function isReviewGateExemptBranch(branch) {
|
|
2391
2401
|
return branch.startsWith("plan/");
|
|
2392
2402
|
}
|
|
2403
|
+
function isBoundToHead(extraction, headSha) {
|
|
2404
|
+
if (!extraction.headSha)
|
|
2405
|
+
return false;
|
|
2406
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2407
|
+
}
|
|
2393
2408
|
function checkReviewGate(input) {
|
|
2394
2409
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2395
2410
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2412,18 +2427,26 @@ function checkReviewGate(input) {
|
|
|
2412
2427
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2413
2428
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2414
2429
|
const securityClean = security.value === "PASS";
|
|
2415
|
-
|
|
2430
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2431
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2432
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2416
2433
|
return {
|
|
2417
2434
|
verdict: "pass",
|
|
2418
|
-
reason:
|
|
2435
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2419
2436
|
waived: false
|
|
2420
2437
|
};
|
|
2421
2438
|
}
|
|
2422
2439
|
const problems = [];
|
|
2423
|
-
if (!codeReviewClean)
|
|
2440
|
+
if (!codeReviewClean) {
|
|
2424
2441
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2425
|
-
if (!
|
|
2442
|
+
} else if (!codeReviewBound) {
|
|
2443
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2444
|
+
}
|
|
2445
|
+
if (!securityClean) {
|
|
2426
2446
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2447
|
+
} else if (!securityBound) {
|
|
2448
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2449
|
+
}
|
|
2427
2450
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2428
2451
|
return {
|
|
2429
2452
|
verdict: "fail",
|
|
@@ -2369,15 +2369,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2369
2369
|
return { ok: true, expectedIssue };
|
|
2370
2370
|
}
|
|
2371
2371
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2372
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2373
|
+
function extractHeadSha(comment) {
|
|
2374
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2375
|
+
return m ? m[1].toLowerCase() : null;
|
|
2376
|
+
}
|
|
2372
2377
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2373
2378
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2374
2379
|
if (clearHits.length > 0) {
|
|
2375
2380
|
const latest = clearHits[clearHits.length - 1];
|
|
2376
2381
|
const m = latest.match(valuePattern);
|
|
2377
|
-
return {
|
|
2382
|
+
return {
|
|
2383
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2384
|
+
headSha: extractHeadSha(latest),
|
|
2385
|
+
danglingNote: null
|
|
2386
|
+
};
|
|
2378
2387
|
}
|
|
2379
2388
|
return {
|
|
2380
2389
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2390
|
+
headSha: null,
|
|
2381
2391
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2382
2392
|
};
|
|
2383
2393
|
}
|
|
@@ -2391,6 +2401,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2391
2401
|
function isReviewGateExemptBranch(branch) {
|
|
2392
2402
|
return branch.startsWith("plan/");
|
|
2393
2403
|
}
|
|
2404
|
+
function isBoundToHead(extraction, headSha) {
|
|
2405
|
+
if (!extraction.headSha)
|
|
2406
|
+
return false;
|
|
2407
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2408
|
+
}
|
|
2394
2409
|
function checkReviewGate(input) {
|
|
2395
2410
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2396
2411
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2413,18 +2428,26 @@ function checkReviewGate(input) {
|
|
|
2413
2428
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2414
2429
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2415
2430
|
const securityClean = security.value === "PASS";
|
|
2416
|
-
|
|
2431
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2432
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2433
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2417
2434
|
return {
|
|
2418
2435
|
verdict: "pass",
|
|
2419
|
-
reason:
|
|
2436
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2420
2437
|
waived: false
|
|
2421
2438
|
};
|
|
2422
2439
|
}
|
|
2423
2440
|
const problems = [];
|
|
2424
|
-
if (!codeReviewClean)
|
|
2441
|
+
if (!codeReviewClean) {
|
|
2425
2442
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2426
|
-
if (!
|
|
2443
|
+
} else if (!codeReviewBound) {
|
|
2444
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2445
|
+
}
|
|
2446
|
+
if (!securityClean) {
|
|
2427
2447
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2448
|
+
} else if (!securityBound) {
|
|
2449
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2450
|
+
}
|
|
2428
2451
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2429
2452
|
return {
|
|
2430
2453
|
verdict: "fail",
|
|
@@ -3148,7 +3171,8 @@ var VinayaConfigSchema = z2.object({
|
|
|
3148
3171
|
briefSchema: BriefSchemaSchema.optional(),
|
|
3149
3172
|
managed: ManagedManifestSchema.optional(),
|
|
3150
3173
|
principals: z2.array(z2.string()).min(1).optional(),
|
|
3151
|
-
ci: z2.object({ setup: z2.string().min(1) }).optional()
|
|
3174
|
+
ci: z2.object({ setup: z2.string().min(1) }).optional(),
|
|
3175
|
+
blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional()
|
|
3152
3176
|
});
|
|
3153
3177
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
3154
3178
|
var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
|
|
@@ -2369,15 +2369,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2369
2369
|
return { ok: true, expectedIssue };
|
|
2370
2370
|
}
|
|
2371
2371
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2372
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2373
|
+
function extractHeadSha(comment) {
|
|
2374
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2375
|
+
return m ? m[1].toLowerCase() : null;
|
|
2376
|
+
}
|
|
2372
2377
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2373
2378
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2374
2379
|
if (clearHits.length > 0) {
|
|
2375
2380
|
const latest = clearHits[clearHits.length - 1];
|
|
2376
2381
|
const m = latest.match(valuePattern);
|
|
2377
|
-
return {
|
|
2382
|
+
return {
|
|
2383
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2384
|
+
headSha: extractHeadSha(latest),
|
|
2385
|
+
danglingNote: null
|
|
2386
|
+
};
|
|
2378
2387
|
}
|
|
2379
2388
|
return {
|
|
2380
2389
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2390
|
+
headSha: null,
|
|
2381
2391
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2382
2392
|
};
|
|
2383
2393
|
}
|
|
@@ -2391,6 +2401,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2391
2401
|
function isReviewGateExemptBranch(branch) {
|
|
2392
2402
|
return branch.startsWith("plan/");
|
|
2393
2403
|
}
|
|
2404
|
+
function isBoundToHead(extraction, headSha) {
|
|
2405
|
+
if (!extraction.headSha)
|
|
2406
|
+
return false;
|
|
2407
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2408
|
+
}
|
|
2394
2409
|
function checkReviewGate(input) {
|
|
2395
2410
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2396
2411
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2413,18 +2428,26 @@ function checkReviewGate(input) {
|
|
|
2413
2428
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2414
2429
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2415
2430
|
const securityClean = security.value === "PASS";
|
|
2416
|
-
|
|
2431
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2432
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2433
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2417
2434
|
return {
|
|
2418
2435
|
verdict: "pass",
|
|
2419
|
-
reason:
|
|
2436
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2420
2437
|
waived: false
|
|
2421
2438
|
};
|
|
2422
2439
|
}
|
|
2423
2440
|
const problems = [];
|
|
2424
|
-
if (!codeReviewClean)
|
|
2441
|
+
if (!codeReviewClean) {
|
|
2425
2442
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2426
|
-
if (!
|
|
2443
|
+
} else if (!codeReviewBound) {
|
|
2444
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2445
|
+
}
|
|
2446
|
+
if (!securityClean) {
|
|
2427
2447
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2448
|
+
} else if (!securityBound) {
|
|
2449
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2450
|
+
}
|
|
2428
2451
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2429
2452
|
return {
|
|
2430
2453
|
verdict: "fail",
|
|
@@ -3149,7 +3172,8 @@ var VinayaConfigSchema = z.object({
|
|
|
3149
3172
|
briefSchema: BriefSchemaSchema.optional(),
|
|
3150
3173
|
managed: ManagedManifestSchema.optional(),
|
|
3151
3174
|
principals: z.array(z.string()).min(1).optional(),
|
|
3152
|
-
ci: z.object({ setup: z.string().min(1) }).optional()
|
|
3175
|
+
ci: z.object({ setup: z.string().min(1) }).optional(),
|
|
3176
|
+
blastRadius: z.object({ extraDomains: z.array(z.string()).optional() }).optional()
|
|
3153
3177
|
});
|
|
3154
3178
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
3155
3179
|
var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
|
|
@@ -2370,15 +2370,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2370
2370
|
return { ok: true, expectedIssue };
|
|
2371
2371
|
}
|
|
2372
2372
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2373
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2374
|
+
function extractHeadSha(comment) {
|
|
2375
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2376
|
+
return m ? m[1].toLowerCase() : null;
|
|
2377
|
+
}
|
|
2373
2378
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2374
2379
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2375
2380
|
if (clearHits.length > 0) {
|
|
2376
2381
|
const latest = clearHits[clearHits.length - 1];
|
|
2377
2382
|
const m = latest.match(valuePattern);
|
|
2378
|
-
return {
|
|
2383
|
+
return {
|
|
2384
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2385
|
+
headSha: extractHeadSha(latest),
|
|
2386
|
+
danglingNote: null
|
|
2387
|
+
};
|
|
2379
2388
|
}
|
|
2380
2389
|
return {
|
|
2381
2390
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2391
|
+
headSha: null,
|
|
2382
2392
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2383
2393
|
};
|
|
2384
2394
|
}
|
|
@@ -2392,6 +2402,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2392
2402
|
function isReviewGateExemptBranch(branch) {
|
|
2393
2403
|
return branch.startsWith("plan/");
|
|
2394
2404
|
}
|
|
2405
|
+
function isBoundToHead(extraction, headSha) {
|
|
2406
|
+
if (!extraction.headSha)
|
|
2407
|
+
return false;
|
|
2408
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2409
|
+
}
|
|
2395
2410
|
function checkReviewGate(input) {
|
|
2396
2411
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2397
2412
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2414,18 +2429,26 @@ function checkReviewGate(input) {
|
|
|
2414
2429
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2415
2430
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2416
2431
|
const securityClean = security.value === "PASS";
|
|
2417
|
-
|
|
2432
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2433
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2434
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2418
2435
|
return {
|
|
2419
2436
|
verdict: "pass",
|
|
2420
|
-
reason:
|
|
2437
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2421
2438
|
waived: false
|
|
2422
2439
|
};
|
|
2423
2440
|
}
|
|
2424
2441
|
const problems = [];
|
|
2425
|
-
if (!codeReviewClean)
|
|
2442
|
+
if (!codeReviewClean) {
|
|
2426
2443
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2427
|
-
if (!
|
|
2444
|
+
} else if (!codeReviewBound) {
|
|
2445
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2446
|
+
}
|
|
2447
|
+
if (!securityClean) {
|
|
2428
2448
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2449
|
+
} else if (!securityBound) {
|
|
2450
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2451
|
+
}
|
|
2429
2452
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2430
2453
|
return {
|
|
2431
2454
|
verdict: "fail",
|
|
@@ -3150,7 +3173,8 @@ var VinayaConfigSchema = z.object({
|
|
|
3150
3173
|
briefSchema: BriefSchemaSchema.optional(),
|
|
3151
3174
|
managed: ManagedManifestSchema.optional(),
|
|
3152
3175
|
principals: z.array(z.string()).min(1).optional(),
|
|
3153
|
-
ci: z.object({ setup: z.string().min(1) }).optional()
|
|
3176
|
+
ci: z.object({ setup: z.string().min(1) }).optional(),
|
|
3177
|
+
blastRadius: z.object({ extraDomains: z.array(z.string()).optional() }).optional()
|
|
3154
3178
|
});
|
|
3155
3179
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
3156
3180
|
var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
|
|
@@ -2368,15 +2368,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2368
2368
|
return { ok: true, expectedIssue };
|
|
2369
2369
|
}
|
|
2370
2370
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2371
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2372
|
+
function extractHeadSha(comment) {
|
|
2373
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2374
|
+
return m ? m[1].toLowerCase() : null;
|
|
2375
|
+
}
|
|
2371
2376
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2372
2377
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2373
2378
|
if (clearHits.length > 0) {
|
|
2374
2379
|
const latest = clearHits[clearHits.length - 1];
|
|
2375
2380
|
const m = latest.match(valuePattern);
|
|
2376
|
-
return {
|
|
2381
|
+
return {
|
|
2382
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2383
|
+
headSha: extractHeadSha(latest),
|
|
2384
|
+
danglingNote: null
|
|
2385
|
+
};
|
|
2377
2386
|
}
|
|
2378
2387
|
return {
|
|
2379
2388
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2389
|
+
headSha: null,
|
|
2380
2390
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2381
2391
|
};
|
|
2382
2392
|
}
|
|
@@ -2390,6 +2400,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2390
2400
|
function isReviewGateExemptBranch(branch) {
|
|
2391
2401
|
return branch.startsWith("plan/");
|
|
2392
2402
|
}
|
|
2403
|
+
function isBoundToHead(extraction, headSha) {
|
|
2404
|
+
if (!extraction.headSha)
|
|
2405
|
+
return false;
|
|
2406
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2407
|
+
}
|
|
2393
2408
|
function checkReviewGate(input) {
|
|
2394
2409
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2395
2410
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2412,18 +2427,26 @@ function checkReviewGate(input) {
|
|
|
2412
2427
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2413
2428
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2414
2429
|
const securityClean = security.value === "PASS";
|
|
2415
|
-
|
|
2430
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2431
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2432
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2416
2433
|
return {
|
|
2417
2434
|
verdict: "pass",
|
|
2418
|
-
reason:
|
|
2435
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2419
2436
|
waived: false
|
|
2420
2437
|
};
|
|
2421
2438
|
}
|
|
2422
2439
|
const problems = [];
|
|
2423
|
-
if (!codeReviewClean)
|
|
2440
|
+
if (!codeReviewClean) {
|
|
2424
2441
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2425
|
-
if (!
|
|
2442
|
+
} else if (!codeReviewBound) {
|
|
2443
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2444
|
+
}
|
|
2445
|
+
if (!securityClean) {
|
|
2426
2446
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2447
|
+
} else if (!securityBound) {
|
|
2448
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2449
|
+
}
|
|
2427
2450
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2428
2451
|
return {
|
|
2429
2452
|
verdict: "fail",
|