@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
@@ -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",
@@ -3147,7 +3170,8 @@ var VinayaConfigSchema = z2.object({
3147
3170
  briefSchema: BriefSchemaSchema.optional(),
3148
3171
  managed: ManagedManifestSchema.optional(),
3149
3172
  principals: z2.array(z2.string()).min(1).optional(),
3150
- ci: z2.object({ setup: z2.string().min(1) }).optional()
3173
+ ci: z2.object({ setup: z2.string().min(1) }).optional(),
3174
+ blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional()
3151
3175
  });
3152
3176
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
3153
3177
  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",
@@ -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",
@@ -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",
@@ -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",
@@ -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",
@@ -3133,7 +3156,8 @@ var VinayaConfigSchema = z.object({
3133
3156
  briefSchema: BriefSchemaSchema.optional(),
3134
3157
  managed: ManagedManifestSchema.optional(),
3135
3158
  principals: z.array(z.string()).min(1).optional(),
3136
- ci: z.object({ setup: z.string().min(1) }).optional()
3159
+ ci: z.object({ setup: z.string().min(1) }).optional(),
3160
+ blastRadius: z.object({ extraDomains: z.array(z.string()).optional() }).optional()
3137
3161
  });
3138
3162
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
3139
3163
  var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
@@ -3219,7 +3243,7 @@ function git(args) {
3219
3243
  }
3220
3244
  function fetchPr(prNumber) {
3221
3245
  try {
3222
- const out = execFileSync3("gh", ["pr", "view", String(prNumber), "--json", "number,comments,labels"], {
3246
+ const out = execFileSync3("gh", ["pr", "view", String(prNumber), "--json", "number,comments,labels,headRefOid"], {
3223
3247
  encoding: "utf8",
3224
3248
  stdio: ["ignore", "pipe", "pipe"]
3225
3249
  });
@@ -3269,7 +3293,8 @@ function main() {
3269
3293
  comments: pr.comments.map((c) => ({ body: c.body, author: c.author?.login ?? null })),
3270
3294
  labels,
3271
3295
  waiverLabelActor,
3272
- principalAllowlist: resolvePrincipalAllowlist(loadTrustAnchorConfig())
3296
+ principalAllowlist: resolvePrincipalAllowlist(loadTrustAnchorConfig()),
3297
+ headSha: pr.headRefOid
3273
3298
  });
3274
3299
  if (result.verdict === "fail") {
3275
3300
  emitCheckError({
@@ -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",
@@ -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",