@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.
Files changed (94) hide show
  1. package/README.md +26 -2
  2. package/aeg-root/contracts/planner-brief.md +1 -1
  3. package/aeg-root/enforcement.md +5 -5
  4. package/aeg-root/roles/planner.md +2 -2
  5. package/aeg-root/roles/reviewer.md +17 -4
  6. package/aeg-root/roles/security.md +16 -5
  7. package/aeg-root/skills/aeg/SKILL.md +1 -1
  8. package/aeg-root/state-machine.md +2 -2
  9. package/aeg-root/tranche-model.md +1 -1
  10. package/dist/checks/bin/check-branch-topology.js +28 -5
  11. package/dist/checks/bin/check-brief-shape.js +28 -5
  12. package/dist/checks/bin/check-closes-n.js +28 -5
  13. package/dist/checks/bin/check-coherence.js +30 -6
  14. package/dist/checks/bin/check-dead-branch-push.js +28 -5
  15. package/dist/checks/bin/check-dispatch-readiness.js +30 -6
  16. package/dist/checks/bin/check-doc-coverage-push.js +30 -6
  17. package/dist/checks/bin/check-doc-coverage.js +30 -6
  18. package/dist/checks/bin/check-evidence-fresh.js +28 -5
  19. package/dist/checks/bin/check-first-push-dispatch.js +30 -6
  20. package/dist/checks/bin/check-issue-assignment.js +28 -5
  21. package/dist/checks/bin/check-no-disk-state.js +28 -5
  22. package/dist/checks/bin/check-reader-resolvable-prose.js +28 -5
  23. package/dist/checks/bin/check-registry-gates.js +28 -5
  24. package/dist/checks/bin/check-review-gate.js +33 -8
  25. package/dist/checks/bin/check-single-plan-pr.js +28 -5
  26. package/dist/checks/bin/check-test-plan.js +28 -5
  27. package/dist/index.js +777 -73
  28. package/package.json +1 -1
  29. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  30. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  31. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  32. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  33. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  34. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  35. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  36. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  37. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  38. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  39. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  40. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  41. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  42. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  43. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  44. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  45. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  46. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  47. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  48. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  49. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  50. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  51. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  52. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  53. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  54. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__0e95dv5._.js +1 -1
  66. 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
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__029-1ch._.js +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0q56ohl._.js +1 -1
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0zfjc2o._.js +1 -1
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1_ojzp1._.js +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1d_8nm4._.js +1 -1
  72. 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
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1zkwtx8._.js +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__210ehg9._.js +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_00yjcv1._.js +1 -1
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0o54suy._.js +1 -1
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0rix02i._.js +1 -1
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_18-d2be._.js +1 -1
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +1 -1
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1gw6vc9._.js +1 -1
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  85. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  86. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2ieu4oi-pp_bz.js → 0ek6qlqhokys0.js} +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1khy4oilrab6g.js → 0fm4p9os2lsom.js} +1 -1
  88. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1eo32v1tbjy7c.js → 0ywkq74falx-x.js} +1 -1
  89. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1t7yy_s34022p.js → 1s9ixp5nhnevp.js} +1 -1
  90. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/27sqffqm2wbxx.css +1 -0
  91. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/0qu0xgvifbacu.css +0 -1
  92. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{ZHVE9Y9G8XKR91g27SgVX → R7xvB2iuNFp_jxENpL7xv}/_buildManifest.js +0 -0
  93. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{ZHVE9Y9G8XKR91g27SgVX → R7xvB2iuNFp_jxENpL7xv}/_clientMiddlewareManifest.js +0 -0
  94. /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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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 { value: m[1].toUpperCase().replace(/[_-]/g, " "), danglingNote: null };
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
- if (codeReviewClean && securityClean) {
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: "code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS.",
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 (!securityClean)
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",