@attalabs/vinaya 0.27.0 → 0.29.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 (143) hide show
  1. package/README.md +5 -4
  2. package/aeg-root/contracts/planner-developer.md +4 -1
  3. package/aeg-root/contracts/planner-operator.md +86 -0
  4. package/aeg-root/contracts/principal-operator.md +88 -0
  5. package/aeg-root/enforcement.md +4 -4
  6. package/aeg-root/roles/developer.md +25 -18
  7. package/aeg-root/roles/operator.md +133 -0
  8. package/aeg-root/roles/planner.md +6 -0
  9. package/aeg-root/roles/principal.md +4 -0
  10. package/aeg-root/roles/reviewer.md +3 -3
  11. package/aeg-root/roles/security.md +3 -3
  12. package/aeg-root/skills/aeg-context-packets/SKILL.md +123 -0
  13. package/aeg-root/skills/aeg-context-packets/examples/operator-start.md +30 -0
  14. package/aeg-root/templates/brief-template.md +6 -2
  15. package/dist/checks/bin/check-body-bare-digits.js +1275 -240
  16. package/dist/checks/bin/check-branch-topology.js +1207 -180
  17. package/dist/checks/bin/check-brief-shape.js +1279 -243
  18. package/dist/checks/bin/check-changeset-coverage.js +2176 -264
  19. package/dist/checks/bin/check-closes-n.js +1211 -184
  20. package/dist/checks/bin/check-coherence.js +1378 -265
  21. package/dist/checks/bin/check-dead-branch-push.js +1158 -176
  22. package/dist/checks/bin/check-dispatch-readiness.js +1440 -276
  23. package/dist/checks/bin/check-doc-coverage-push.js +2171 -259
  24. package/dist/checks/bin/check-doc-coverage.js +2173 -261
  25. package/dist/checks/bin/check-doctrine-no-procedures.js +1275 -240
  26. package/dist/checks/bin/check-doctrine-portability.js +2171 -259
  27. package/dist/checks/bin/check-evidence-fresh.js +2613 -347
  28. package/dist/checks/bin/check-exec-bits.js +2169 -257
  29. package/dist/checks/bin/check-first-push-dispatch.js +1326 -246
  30. package/dist/checks/bin/check-issue-assignment.js +1209 -182
  31. package/dist/checks/bin/check-issue-milestone-attach.js +6087 -0
  32. package/dist/checks/bin/check-issue-objectives-numbering.js +6089 -0
  33. package/dist/checks/bin/check-issue-parts-coverage.js +6089 -0
  34. package/dist/checks/bin/check-issue-surface-globs.js +7304 -0
  35. package/dist/checks/bin/check-issue-title-grammar.js +6089 -0
  36. package/dist/checks/bin/check-issue-tranche-label.js +6089 -0
  37. package/dist/checks/bin/check-main-branch-refusal.js +1158 -176
  38. package/dist/checks/bin/check-no-disk-state.js +1158 -176
  39. package/dist/checks/bin/check-pr-premise-reassert.js +1275 -240
  40. package/dist/checks/bin/check-pr-report-density.js +1158 -176
  41. package/dist/checks/bin/check-quoted-command.js +2150 -257
  42. package/dist/checks/bin/check-reader-resolvable-prose.js +2153 -260
  43. package/dist/checks/bin/check-registry-gates.js +1245 -176
  44. package/dist/checks/bin/check-retired-vocabulary.js +2148 -255
  45. package/dist/checks/bin/check-review-gate.js +1375 -245
  46. package/dist/checks/bin/check-single-plan-pr.js +1158 -176
  47. package/dist/checks/bin/check-surface-scope.js +1217 -182
  48. package/dist/checks/bin/check-test-plan.js +1187 -189
  49. package/dist/checks/bin/check-token-collection-wired.js +1158 -176
  50. package/dist/checks/bin/check-token-report.js +1171 -179
  51. package/dist/checks/bin/check-workspace-escape.js +2167 -255
  52. package/dist/index.js +17322 -8709
  53. package/dist/lib/pre-push-changed-files.js +57 -0
  54. package/dist/lib/pre-push-select-tests.js +1616 -0
  55. package/package.json +4 -2
  56. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  57. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  58. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error/page.js +2 -1
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js +8 -7
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js +8 -7
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js +9 -8
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js +9 -8
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  85. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js +8 -7
  86. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  88. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +16 -4
  89. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js +10 -8
  90. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  91. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  92. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  93. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js +8 -7
  94. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  95. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  96. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  97. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js +8 -7
  98. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  99. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  100. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  101. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js +9 -8
  102. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  103. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  104. 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
  105. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__08f5pba._.js +3 -0
  106. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1o_0tjy._.js → [root-of-the-server]__0h9yhwx._.js} +2 -2
  107. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0053k9k._.js → [root-of-the-server]__0ls23oh._.js} +3 -2
  108. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0puovz5._.js → [root-of-the-server]__0nye8tb._.js} +3 -2
  109. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__18kqy4q._.js → [root-of-the-server]__0tfykr-._.js} +2 -2
  110. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0o771t1._.js → [root-of-the-server]__0th3gyp._.js} +3 -2
  111. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1-zcufa._.js +51 -0
  112. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0p8q38b._.js → [root-of-the-server]__11svemm._.js} +1 -1
  113. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__02c71kv._.js → [root-of-the-server]__140m1i_._.js} +2 -2
  114. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1bh2awk._.js +3 -0
  115. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0112h-k._.js → [root-of-the-server]__1gjqmhq._.js} +3 -2
  116. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1qmxez3._.js +4 -0
  117. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1r47dr7._.js +28 -0
  118. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  119. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_01fv4fj._.js +1 -1
  120. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_0gvm3og._.js → _096f46i._.js} +3 -2
  121. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_0lwxg63._.js → _0af2one._.js} +3 -2
  122. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_1n0cnq-._.js → _0zf4d27._.js} +45 -2
  123. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_03x_w6q._.js → _1-wo68i._.js} +3 -2
  124. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_app_studio_projects_[name]_tranches_[slug]_05ufo8i._.js +1 -12
  125. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1wdusod._.js +3 -0
  126. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{node_modules_1vo08dj._.js → node_modules_@clerk_nextjs_dist_esm_app-router_server-actions_1x7vfhn.js} +3 -4
  127. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_next_1khj-jh._.js +3 -0
  128. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  129. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  130. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  131. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +17 -5
  132. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3_9xytdmiv1xi.js → 25f3-jtiv8p5n.js} +4 -4
  133. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0bn8c8v5q429o.js → 27-6ym7lp2v8o.js} +1 -1
  134. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3-6a3tinbdb-7.js → 3daxns3o--9me.js} +1 -1
  135. package/studio-standalone/apps/vinaya-studio/web/tsconfig.json +12 -0
  136. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0z6dav-._.js +0 -51
  137. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__13rrmhr._.js +0 -3
  138. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__17h4sy8._.js +0 -45
  139. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +0 -3
  140. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_next_17sz44y._.js +0 -3
  141. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_buildManifest.js +0 -0
  142. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_clientMiddlewareManifest.js +0 -0
  143. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_ssgManifest.js +0 -0
@@ -3,8 +3,8 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/checks/bin/check-retired-vocabulary.ts
6
- import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync } from "node:fs";
7
- import { join as join5 } from "node:path";
6
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync } from "node:fs";
7
+ import { join as join7 } from "node:path";
8
8
  // ../../packages/aeg-core/src/gate-audience.ts
9
9
  var GATE_AUDIENCE = {
10
10
  "check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
@@ -1645,6 +1645,52 @@ var systemEnv3 = {
1645
1645
  ...process.env,
1646
1646
  PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].filter(Boolean).join(":")
1647
1647
  };
1648
+ var defaultExec = () => execFileAsync3("git", ["remote", "get-url", "origin"], { env: systemEnv3, timeout: 5000 });
1649
+ var cached;
1650
+ var inflight;
1651
+ async function resolveRepo(exec = defaultExec) {
1652
+ if (cached !== undefined)
1653
+ return cached;
1654
+ const fromEnv = process.env.AEG_REPO;
1655
+ if (fromEnv) {
1656
+ const parsed = parseOwnerRepo(fromEnv);
1657
+ if (parsed) {
1658
+ cached = parsed;
1659
+ return cached;
1660
+ }
1661
+ }
1662
+ if (inflight)
1663
+ return inflight;
1664
+ inflight = (async () => {
1665
+ try {
1666
+ const { stdout } = await exec();
1667
+ cached = parseGitRemoteUrl(stdout.trim());
1668
+ return cached;
1669
+ } catch (err) {
1670
+ const message = err instanceof Error ? err.message : String(err);
1671
+ console.warn(`[aeg-forge-state] resolveRepo: git remote lookup failed (will retry): ${message}`);
1672
+ return null;
1673
+ } finally {
1674
+ inflight = undefined;
1675
+ }
1676
+ })();
1677
+ return inflight;
1678
+ }
1679
+ function parseGitRemoteUrl(url) {
1680
+ const ssh = url.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
1681
+ if (ssh?.[1] && ssh[2])
1682
+ return { owner: ssh[1], repo: ssh[2] };
1683
+ const https = url.match(/^https?:\/\/(?:[^@]+@)?github\.com\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
1684
+ if (https?.[1] && https[2])
1685
+ return { owner: https[1], repo: https[2] };
1686
+ return null;
1687
+ }
1688
+ function parseOwnerRepo(value) {
1689
+ const match = value.match(/^([^/]+)\/(.+)$/);
1690
+ if (!match?.[1] || !match[2])
1691
+ return null;
1692
+ return { owner: match[1], repo: match[2] };
1693
+ }
1648
1694
  // ../../packages/aeg-core/src/anchored-region.ts
1649
1695
  function anchoredRegionBounds(body, field) {
1650
1696
  const masked = maskCode(body);
@@ -1955,6 +2001,9 @@ function parseInlineFieldList(section) {
1955
2001
  var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
1956
2002
  var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1957
2003
  var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
2004
+ var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
2005
+ var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
2006
+ var BASE_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged base:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
1958
2007
  function firstFiveLines(comment) {
1959
2008
  return comment.split(`
1960
2009
  `).slice(0, 5).join(`
@@ -1977,6 +2026,35 @@ function extractRulingOrdinal(comment) {
1977
2026
  const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
1978
2027
  return m ? Number.parseInt(m[1], 10) : null;
1979
2028
  }
2029
+ function firstElevenLines(comment) {
2030
+ return comment.split(`
2031
+ `).slice(0, 11).join(`
2032
+ `);
2033
+ }
2034
+ function extractBriefHash(comment) {
2035
+ const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
2036
+ return m ? m[1].toLowerCase() : null;
2037
+ }
2038
+ function extractPolicyDigest(comment) {
2039
+ const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
2040
+ return m ? m[1].toLowerCase() : null;
2041
+ }
2042
+ function firstThirteenLines(comment) {
2043
+ return comment.split(`
2044
+ `).slice(0, 13).join(`
2045
+ `);
2046
+ }
2047
+ function extractBaseSha(comment) {
2048
+ const m = firstThirteenLines(comment).match(BASE_SHA_PATTERN);
2049
+ return m ? m[1].toLowerCase() : null;
2050
+ }
2051
+ var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
2052
+ function extractFindingSeverities(comment) {
2053
+ return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
2054
+ severity: m[1],
2055
+ location: m[2]
2056
+ }));
2057
+ }
1980
2058
  function extractVerdict(comments, valuePattern, missingLabel) {
1981
2059
  const candidates = comments.filter((c) => valuePattern.test(c));
1982
2060
  if (candidates.length === 0) {
@@ -1985,6 +2063,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
1985
2063
  headSha: null,
1986
2064
  objectivesVersion: null,
1987
2065
  rulingOrdinal: null,
2066
+ briefHash: null,
2067
+ policyDigest: null,
2068
+ baseSha: null,
2069
+ findingSeverities: [],
1988
2070
  danglingNote: `no ${missingLabel} verdict comment found on this PR`
1989
2071
  };
1990
2072
  }
@@ -1996,6 +2078,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
1996
2078
  headSha: null,
1997
2079
  objectivesVersion: null,
1998
2080
  rulingOrdinal: null,
2081
+ briefHash: null,
2082
+ policyDigest: null,
2083
+ baseSha: null,
2084
+ findingSeverities: [],
1999
2085
  danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
2000
2086
  };
2001
2087
  }
@@ -2004,6 +2090,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2004
2090
  headSha: extractHeadSha(latest),
2005
2091
  objectivesVersion: extractObjectivesVersion(latest),
2006
2092
  rulingOrdinal: extractRulingOrdinal(latest),
2093
+ briefHash: extractBriefHash(latest),
2094
+ policyDigest: extractPolicyDigest(latest),
2095
+ baseSha: extractBaseSha(latest),
2096
+ findingSeverities: extractFindingSeverities(latest),
2007
2097
  danglingNote: null
2008
2098
  };
2009
2099
  }
@@ -2339,6 +2429,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
2339
2429
  }
2340
2430
  return out;
2341
2431
  }
2432
+ // ../../packages/aeg-core/src/review-policy.ts
2433
+ var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
2434
+ var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
2435
+ var DEFAULT_MAX_ROUNDS = 3;
2436
+ var DEFAULT_REVIEW_POLICY = {
2437
+ codeReviewThreshold: "BLOCKER",
2438
+ securityThreshold: "HIGH",
2439
+ maxRounds: DEFAULT_MAX_ROUNDS
2440
+ };
2441
+ var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
2442
+ var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
2443
+ var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
2444
+ function isProseLocation(location) {
2445
+ if (ROLE_FILE_LOCATION.test(location))
2446
+ return true;
2447
+ if (FILE_SHAPED_LOCATION.test(location))
2448
+ return false;
2449
+ return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
2450
+ }
2451
+ var PROSE_CAP_SEVERITY = "MINOR";
2452
+ function blockingSeverities(scale, threshold) {
2453
+ const idx = scale.indexOf(threshold);
2454
+ if (idx === -1) {
2455
+ throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
2456
+ }
2457
+ return scale.slice(0, idx + 1);
2458
+ }
2459
+ function evaluateReviewFindings(findings, scale, threshold) {
2460
+ const blocking = new Set(blockingSeverities(scale, threshold));
2461
+ const blockingFindings = findings.filter((f) => {
2462
+ if (!scale.includes(f.severity)) {
2463
+ throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
2464
+ }
2465
+ const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
2466
+ return blocking.has(effectiveSeverity);
2467
+ });
2468
+ return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
2469
+ }
2470
+ function evaluateCodeReview(findings, policy) {
2471
+ return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
2472
+ }
2473
+ function evaluateSecurityReview(findings, policy) {
2474
+ return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
2475
+ }
2476
+ function isKnownSeverity(scale, value) {
2477
+ return scale.includes(value);
2478
+ }
2342
2479
  // ../../packages/aeg-core/src/pr-tier.ts
2343
2480
  var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
2344
2481
  function readTierFromPrBody(prBody) {
@@ -2709,7 +2846,7 @@ function checkForField(prBody) {
2709
2846
  ]
2710
2847
  };
2711
2848
  }
2712
- function checkClosesN(prBody) {
2849
+ function checkClosesNPresence(prBody) {
2713
2850
  const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
2714
2851
  if (closesPattern.test(stripCode(prBody))) {
2715
2852
  return { status: "pass", errors: [] };
@@ -2741,10 +2878,22 @@ var COMMIT_TYPES = [
2741
2878
  "Test"
2742
2879
  ];
2743
2880
  var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
2881
+ function checkForgeTitle(title) {
2882
+ const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
2883
+ if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
2884
+ return { status: "pass", errors: [] };
2885
+ return {
2886
+ status: "fail",
2887
+ errors: [
2888
+ `brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
2889
+ ]
2890
+ };
2891
+ }
2744
2892
  var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
2745
2893
  var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
2894
+ var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
2746
2895
  function isTaskBranch(branch) {
2747
- return TASK_BRANCH_PATTERN.test(branch);
2896
+ return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
2748
2897
  }
2749
2898
  function isBriefShaped(prBody) {
2750
2899
  const stripped = stripCode(prBody);
@@ -2866,8 +3015,9 @@ function packagesNamedIn(text) {
2866
3015
  }
2867
3016
  function hasTestPathForConsumer(text, consumerDir) {
2868
3017
  const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2869
- const re = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
2870
- return re.test(text);
3018
+ const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
3019
+ const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
3020
+ return filePathRe.test(text) || testDirRe.test(text);
2871
3021
  }
2872
3022
  function checkConsumerTests(prBody, consumersOf) {
2873
3023
  const section4 = extractNumberedSection(prBody, 4);
@@ -2971,7 +3121,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
2971
3121
  checkConsumerTests(prBody, consumersOf),
2972
3122
  checkDefeatCases(prBody),
2973
3123
  ...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
2974
- ...requireClosesN ? [checkClosesN(prBody)] : []
3124
+ ...requireClosesN ? [checkClosesNPresence(prBody)] : []
2975
3125
  ];
2976
3126
  return { errors: results.flatMap((r) => r.errors) };
2977
3127
  }
@@ -3261,6 +3411,28 @@ function topLevelSectionText(body, headingName) {
3261
3411
  const next = /^##[ \t]/m.exec(afterHeading);
3262
3412
  return next ? afterHeading.slice(0, next.index) : afterHeading;
3263
3413
  }
3414
+ function partHasBacktickedPath(text) {
3415
+ let i = 0;
3416
+ while (i < text.length) {
3417
+ const start = text.indexOf("`", i);
3418
+ if (start === -1)
3419
+ return false;
3420
+ const end = text.indexOf("`", start + 1);
3421
+ if (end === -1)
3422
+ return false;
3423
+ if (text.slice(start + 1, end).includes("/"))
3424
+ return true;
3425
+ i = end + 1;
3426
+ }
3427
+ return false;
3428
+ }
3429
+ var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
3430
+ function stripPartBackticks(text) {
3431
+ return text.replace(/`[^`\n]*`/g, " ");
3432
+ }
3433
+ function partWordCount(text) {
3434
+ return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
3435
+ }
3264
3436
  function looksLikeFilePath(entry2) {
3265
3437
  const stripped = entry2.replace(/\/\*\*?$/, "");
3266
3438
  const lastSegment = stripped.split("/").pop() ?? stripped;
@@ -3301,6 +3473,18 @@ function globCoversPath(glob, path) {
3301
3473
  const p = path.replace(/\/+$/, "");
3302
3474
  return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
3303
3475
  }
3476
+ function checkSurfaceGlobsResolve(body, resolvesToFile) {
3477
+ const surface = parseIssueSurface(body);
3478
+ if (!surface.ok)
3479
+ return { status: "pass", errors: [] };
3480
+ const errors = [];
3481
+ for (const glob of surface.value.in) {
3482
+ if (!resolvesToFile(glob)) {
3483
+ errors.push(`issue-validation Surface: \`${glob}\` in \`## Surface\`'s \`in:\` list matches no tracked file — a Surface that cannot resolve cannot render a brief's file list.`);
3484
+ }
3485
+ }
3486
+ return { status: errors.length > 0 ? "fail" : "pass", errors };
3487
+ }
3304
3488
  function checkSurfaceScope(changedFiles, outGlobs) {
3305
3489
  if (outGlobs.length === 0)
3306
3490
  return { ok: true };
@@ -3312,9 +3496,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
3312
3496
  }
3313
3497
  return violations.length > 0 ? { ok: false, violations } : { ok: true };
3314
3498
  }
3499
+ var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
3500
+ function parseIssueParts(body) {
3501
+ const section = topLevelSectionText(body, "Parts");
3502
+ if (section === null)
3503
+ return { ok: false, errors: ["no `## Parts` heading found in the body."] };
3504
+ const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
3505
+ const parts = [];
3506
+ const errors = [];
3507
+ for (const line of lines) {
3508
+ const m = ISSUE_PART_LINE_RE.exec(line);
3509
+ if (!m) {
3510
+ errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
3511
+ continue;
3512
+ }
3513
+ const n = Number.parseInt(m[1], 10);
3514
+ const refs = m[2];
3515
+ const text = m[3].trim();
3516
+ if (text.length === 0) {
3517
+ errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
3518
+ continue;
3519
+ }
3520
+ if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
3521
+ errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
3522
+ continue;
3523
+ }
3524
+ const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
3525
+ parts.push({ n, objectiveIds, text });
3526
+ }
3527
+ if (parts.length === 0) {
3528
+ errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
3529
+ }
3530
+ if (errors.length > 0)
3531
+ return { ok: false, errors };
3532
+ return { ok: true, value: parts };
3533
+ }
3534
+ function checkPartsCiteDefinedObjectives(body) {
3535
+ const parts = parseIssueParts(body);
3536
+ const objectives = objectivesOf(body);
3537
+ if (!parts.ok || !objectives.ok)
3538
+ return { status: "pass", errors: [] };
3539
+ const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
3540
+ const errors = [];
3541
+ for (const part of parts.value) {
3542
+ for (const objectiveId of part.objectiveIds) {
3543
+ if (!definedIds.has(objectiveId)) {
3544
+ errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
3545
+ }
3546
+ }
3547
+ }
3548
+ return { status: errors.length > 0 ? "fail" : "pass", errors };
3549
+ }
3315
3550
  function isTaskIssueLabelSet(labels) {
3316
3551
  return hasLabel("tranche", labels);
3317
3552
  }
3553
+ function checkTrancheLabelPresence(_body, _labels) {
3554
+ return { status: "pass", errors: [] };
3555
+ }
3556
+ function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
3557
+ if (!isTaskIssueLabelSet(labels))
3558
+ return { status: "pass", errors: [] };
3559
+ if (resolvedMilestoneTitle === null)
3560
+ return { status: "pass", errors: [] };
3561
+ if (currentMilestoneTitle === resolvedMilestoneTitle)
3562
+ return { status: "pass", errors: [] };
3563
+ return {
3564
+ status: "fail",
3565
+ errors: [
3566
+ `issue-validation milestone attach: this task Issue's tranche label resolves to Milestone "${resolvedMilestoneTitle}", but its live Milestone is ${currentMilestoneTitle === null ? "unset" : `"${currentMilestoneTitle}"`} — attach it to "${resolvedMilestoneTitle}".`
3567
+ ]
3568
+ };
3569
+ }
3318
3570
  var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
3319
3571
  function isControlCodePoint(codePoint) {
3320
3572
  return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
@@ -3379,6 +3631,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
3379
3631
  var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
3380
3632
  var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
3381
3633
 
3634
+ // ../../packages/aeg-core/src/task-branch-identity.ts
3635
+ var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
3636
+ var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
3637
+ function parseTaskBranchIdentity(branch) {
3638
+ const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
3639
+ if (issueMatch)
3640
+ return { kind: "issue", issueNumber: Number(issueMatch[1]) };
3641
+ const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
3642
+ if (trancheMatch)
3643
+ return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
3644
+ return null;
3645
+ }
3646
+
3382
3647
  // ../../packages/aeg-core/src/coherence-checks.ts
3383
3648
  var COHERENCE_ENFORCED_FROM = "2026-07-01";
3384
3649
  function isGrandfathered(isoDate) {
@@ -3399,6 +3664,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
3399
3664
  if (handClosed)
3400
3665
  continue;
3401
3666
  failures.push({
3667
+ code: "closed-without-merge",
3402
3668
  issue: e.task.issue,
3403
3669
  tranche: e.trancheSlug,
3404
3670
  task: e.task.id,
@@ -3423,6 +3689,7 @@ function checkA3(entries) {
3423
3689
  continue;
3424
3690
  if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
3425
3691
  failures.push({
3692
+ code: "auto-close-misfire",
3426
3693
  issue: e.task.issue,
3427
3694
  tranche: e.trancheSlug,
3428
3695
  task: e.task.id,
@@ -3447,6 +3714,7 @@ function checkT1(entries) {
3447
3714
  continue;
3448
3715
  if (e.facts === undefined) {
3449
3716
  failures.push({
3717
+ code: "phantom-issue-ref",
3450
3718
  issue: e.task.issue,
3451
3719
  tranche: e.trancheSlug,
3452
3720
  task: e.task.id,
@@ -3465,6 +3733,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
3465
3733
  for (const num of openNums) {
3466
3734
  if (!topologySet.has(num)) {
3467
3735
  failures.push({
3736
+ code: "orphan-task",
3468
3737
  issue: num,
3469
3738
  tranche: slug,
3470
3739
  reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
@@ -3499,6 +3768,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
3499
3768
  continue;
3500
3769
  if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
3501
3770
  failures.push({
3771
+ code: "tbd-in-active-tranche",
3502
3772
  issue: null,
3503
3773
  tranche: e.trancheSlug,
3504
3774
  task: e.task.id,
@@ -3508,6 +3778,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
3508
3778
  continue;
3509
3779
  }
3510
3780
  failures.push({
3781
+ code: "tbd-in-active-tranche",
3511
3782
  issue: null,
3512
3783
  tranche: e.trancheSlug,
3513
3784
  task: e.task.id,
@@ -3540,6 +3811,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
3540
3811
  const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
3541
3812
  if (sameTask || sameIssue) {
3542
3813
  failures.push({
3814
+ code: "d1-self-dependency",
3543
3815
  issue: e.task.issue,
3544
3816
  tranche: e.trancheSlug,
3545
3817
  task: e.task.id,
@@ -3551,6 +3823,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
3551
3823
  const depClosed = depFacts?.issueState === "closed";
3552
3824
  if (!depClosed) {
3553
3825
  failures.push({
3826
+ code: "dispatched-on-unmet-deps",
3554
3827
  issue: e.task.issue,
3555
3828
  tranche: e.trancheSlug,
3556
3829
  task: e.task.id,
@@ -3576,11 +3849,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
3576
3849
  const errors = [
3577
3850
  ...checkIssueRationale(issue.body).errors,
3578
3851
  ...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
3579
- ...checkIssueObjectives(issue.body, issue.number).errors
3852
+ ...checkIssueObjectives(issue.body, issue.number).errors,
3853
+ ...checkPartsCiteDefinedObjectives(issue.body).errors
3580
3854
  ];
3581
3855
  if (errors.length === 0)
3582
3856
  continue;
3583
3857
  failures.push({
3858
+ code: "missing-rationale-field",
3584
3859
  issue: issue.number,
3585
3860
  tranche: slug,
3586
3861
  reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
@@ -3609,6 +3884,7 @@ function checkL1(files, entriesBySlug) {
3609
3884
  const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
3610
3885
  if (allClosed) {
3611
3886
  failures.push({
3887
+ code: "archive-recommended",
3612
3888
  tranche: f.slug,
3613
3889
  reason: "Active tranche has no open task-Issues — consider archiving to completed/"
3614
3890
  });
@@ -3640,27 +3916,38 @@ function extractClosesReferences(prBody) {
3640
3916
  }
3641
3917
  return referenced;
3642
3918
  }
3643
- function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
3919
+ function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
3644
3920
  const referenced = extractClosesReferences(prBody);
3645
3921
  if (taskIssueRefs) {
3646
3922
  for (const n of referenced) {
3647
- const ref = taskIssueRefs.get(n);
3648
- if (!ref)
3923
+ const ref2 = taskIssueRefs.get(n);
3924
+ if (!ref2)
3649
3925
  continue;
3650
- const expectedBranch = `task/${ref.trancheSlug}/${ref.taskId}`;
3926
+ const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
3651
3927
  if (branch !== expectedBranch) {
3652
3928
  return {
3653
3929
  ok: false,
3654
- message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${ref.taskId} of tranche "${ref.trancheSlug}") but is not named "${expectedBranch}" — rename the branch and re-push, or if this work is intentionally outside AEG's dispatch flow, remove the Closes reference.`
3930
+ message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${ref2.taskId} of tranche "${ref2.trancheSlug}") but is not named "${expectedBranch}" — rename the branch and re-push, or if this work is intentionally outside AEG's dispatch flow, remove the Closes reference.`
3655
3931
  };
3656
3932
  }
3657
3933
  }
3658
3934
  }
3659
- const m = branch.match(/^task\/([^/]+)\/([^/]+)$/);
3660
- if (!m)
3935
+ const ref = parseTaskBranchIdentity(branch);
3936
+ if (!ref)
3661
3937
  return { ok: true };
3662
- const trancheSlug = m[1];
3663
- const taskId = m[2];
3938
+ if (ref.kind === "issue") {
3939
+ const expectedIssue2 = ref.issueNumber;
3940
+ if (!referenced.has(expectedIssue2)) {
3941
+ return {
3942
+ ok: false,
3943
+ expectedIssue: expectedIssue2,
3944
+ message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
3945
+ };
3946
+ }
3947
+ return { ok: true, expectedIssue: expectedIssue2 };
3948
+ }
3949
+ const trancheSlug = ref.tranche;
3950
+ const taskId = ref.taskId;
3664
3951
  const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
3665
3952
  if (!trancheFile) {
3666
3953
  return {
@@ -3712,34 +3999,79 @@ function extractIssue(body) {
3712
3999
  const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
3713
4000
  return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
3714
4001
  }
3715
- // ../../packages/aeg-core/src/review-gate.ts
3716
- function isBoundToHead(extraction, headSha) {
3717
- if (!extraction.headSha)
4002
+ // ../../packages/aeg-core/src/review-input-manifest.ts
4003
+ import { createHash as createHash4 } from "node:crypto";
4004
+ function briefHash(brief) {
4005
+ return createHash4("sha256").update(`${brief}
4006
+ `).digest("hex");
4007
+ }
4008
+ function policyDigest(policy) {
4009
+ return createHash4("sha256").update(JSON.stringify({
4010
+ codeReviewThreshold: policy.codeReviewThreshold,
4011
+ securityThreshold: policy.securityThreshold,
4012
+ maxRounds: policy.maxRounds
4013
+ })).digest("hex");
4014
+ }
4015
+ function isBoundToHead(echoed, headSha) {
4016
+ if (!echoed.headSha)
3718
4017
  return false;
3719
- return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
4018
+ return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
3720
4019
  }
3721
- function isBoundByPatchIdentity(extraction, headSha, patchIdOf) {
3722
- if (patchIdOf === undefined || !extraction.headSha)
4020
+ function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
4021
+ if (patchIdOf === undefined || !echoed.headSha)
3723
4022
  return false;
3724
- const judged = patchIdOf(extraction.headSha);
4023
+ const judged = patchIdOf(echoed.headSha);
3725
4024
  const current = patchIdOf(headSha);
3726
4025
  if (judged === null || current === null)
3727
4026
  return false;
3728
4027
  return judged === current;
3729
4028
  }
3730
- function isBoundToPatch(extraction, headSha, patchIdOf) {
3731
- return isBoundToHead(extraction, headSha) || isBoundByPatchIdentity(extraction, headSha, patchIdOf);
4029
+ function isBoundToBase(echoed, currentBase) {
4030
+ if (currentBase === null)
4031
+ return true;
4032
+ if (!echoed.baseSha)
4033
+ return false;
4034
+ return currentBase.toLowerCase().startsWith(echoed.baseSha.toLowerCase());
3732
4035
  }
3733
- function isBoundToObjectives(extraction, currentVersion) {
4036
+ function isBoundToObjectives(echoed, currentVersion) {
3734
4037
  if (currentVersion === null)
3735
4038
  return true;
3736
- return extraction.objectivesVersion === currentVersion;
4039
+ return echoed.objectivesVersion === currentVersion;
3737
4040
  }
3738
- function isBoundToRulings(extraction, currentOrdinal) {
3739
- if (extraction.rulingOrdinal === null)
4041
+ function isBoundToRulings(echoed, currentOrdinal) {
4042
+ if (echoed.rulingOrdinal === null)
3740
4043
  return currentOrdinal === 0;
3741
- return extraction.rulingOrdinal === currentOrdinal;
4044
+ return echoed.rulingOrdinal === currentOrdinal;
3742
4045
  }
4046
+ function isBoundToBriefHash(echoed, currentHash) {
4047
+ if (currentHash === null)
4048
+ return true;
4049
+ return echoed.briefHash === currentHash;
4050
+ }
4051
+ function isBoundToPolicy(echoed, currentDigest) {
4052
+ return echoed.policyDigest === currentDigest;
4053
+ }
4054
+ function compareManifest(echoed, current, patchIdOf) {
4055
+ const exactHead = isBoundToHead(echoed, current.headSha);
4056
+ const patchHead = exactHead ? false : isBoundByPatchIdentity(echoed, current.headSha, patchIdOf);
4057
+ const head = exactHead || patchHead;
4058
+ const base = patchHead ? true : isBoundToBase(echoed, current.baseSha);
4059
+ const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
4060
+ const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
4061
+ const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
4062
+ const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
4063
+ return {
4064
+ bound: head && base && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
4065
+ head,
4066
+ base,
4067
+ briefHash: briefHashBound,
4068
+ objectivesVersion: objectivesVersion2,
4069
+ rulingOrdinal,
4070
+ policyDigest: policyDigestBound
4071
+ };
4072
+ }
4073
+
4074
+ // ../../packages/aeg-core/src/review-gate.ts
3743
4075
  function checkReviewGate(input) {
3744
4076
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
3745
4077
  const waived = isWaiverLabelActorVerified({
@@ -3762,15 +4094,58 @@ function checkReviewGate(input) {
3762
4094
  const verifiedBodies = verified.map((c) => c.body);
3763
4095
  const codeReview = extractCodeReviewVerdict(verifiedBodies);
3764
4096
  const security = extractSecurityReviewVerdict(verifiedBodies);
3765
- const codeReviewClean = codeReview.value === "APPROVE";
3766
- const securityClean = security.value === "PASS";
3767
- const codeReviewBound = isBoundToPatch(codeReview, input.headSha, input.patchIdOf);
3768
- const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
3769
- const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
3770
- const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
3771
- const codeReviewRulingsBound = isBoundToRulings(codeReview, input.rulingOrdinal);
3772
- const securityRulingsBound = isBoundToRulings(security, input.rulingOrdinal);
3773
- if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && codeReviewRulingsBound && securityClean && securityBound && securityObjectivesBound && securityRulingsBound && mechanicalChecksClean) {
4097
+ const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
4098
+ let codeReviewPolicyEvaluation;
4099
+ let securityPolicyEvaluation;
4100
+ try {
4101
+ codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
4102
+ securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
4103
+ } catch (err) {
4104
+ return {
4105
+ verdict: "fail",
4106
+ reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
4107
+ waived: false
4108
+ };
4109
+ }
4110
+ const codeReviewTextClean = codeReview.value === "APPROVE";
4111
+ const securityTextClean = security.value === "PASS";
4112
+ const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
4113
+ const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
4114
+ const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
4115
+ const securityClean = securityTextClean && securityPolicyClean;
4116
+ const currentManifest = {
4117
+ headSha: input.headSha,
4118
+ baseSha: input.baseSha ?? null,
4119
+ briefHash: input.briefHash ?? null,
4120
+ objectivesVersion: input.objectivesVersion,
4121
+ rulingOrdinal: input.rulingOrdinal,
4122
+ policyDigest: policyDigest(policy)
4123
+ };
4124
+ const codeReviewEchoed = {
4125
+ headSha: codeReview.headSha,
4126
+ baseSha: codeReview.baseSha,
4127
+ briefHash: codeReview.briefHash,
4128
+ objectivesVersion: codeReview.objectivesVersion,
4129
+ rulingOrdinal: codeReview.rulingOrdinal,
4130
+ policyDigest: codeReview.policyDigest
4131
+ };
4132
+ const securityEchoed = {
4133
+ headSha: security.headSha,
4134
+ baseSha: security.baseSha,
4135
+ briefHash: security.briefHash,
4136
+ objectivesVersion: security.objectivesVersion,
4137
+ rulingOrdinal: security.rulingOrdinal,
4138
+ policyDigest: security.policyDigest
4139
+ };
4140
+ const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
4141
+ const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
4142
+ const codeReviewBound = codeReviewBinding.head;
4143
+ const securityBound = securityBinding.head;
4144
+ const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
4145
+ const securityObjectivesBound = securityBinding.objectivesVersion;
4146
+ const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
4147
+ const securityRulingsBound = securityBinding.rulingOrdinal;
4148
+ if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
3774
4149
  return {
3775
4150
  verdict: "pass",
3776
4151
  reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}, and every reported mechanical check is green.`,
@@ -3778,23 +4153,39 @@ function checkReviewGate(input) {
3778
4153
  };
3779
4154
  }
3780
4155
  const problems = [];
3781
- if (!codeReviewClean) {
4156
+ if (!codeReviewTextClean) {
3782
4157
  problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
4158
+ } else if (!codeReviewPolicyClean) {
4159
+ problems.push(`code-reviewer verdict says APPROVE but carries a finding (${codeReviewPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's code-review policy threshold (${policy.codeReviewThreshold}) — a reviewer's own APPROVE never overrides policy`);
3783
4160
  } else if (!codeReviewBound) {
3784
4161
  problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
4162
+ } else if (!codeReviewBinding.base) {
4163
+ problems.push(`the newest code-review verdict was cast against base ${codeReview.baseSha ?? "no recorded base"}, the PR's base is now ${currentManifest.baseSha ?? "unresolved"} (a base-only change under an unchanged candidate — same patch text on a new base is not automatically equivalent)`);
3785
4164
  } else if (!codeReviewObjectivesBound) {
3786
4165
  problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
3787
4166
  } else if (!codeReviewRulingsBound) {
3788
4167
  problems.push(`the newest code-review verdict was cast against ruling ordinal ${codeReview.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
4168
+ } else if (!codeReviewBinding.briefHash) {
4169
+ problems.push(`the newest code-review verdict was cast against brief hash ${codeReview.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
4170
+ } else if (!codeReviewBinding.policyDigest) {
4171
+ problems.push(`the newest code-review verdict was cast against review policy digest ${codeReview.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
3789
4172
  }
3790
- if (!securityClean) {
4173
+ if (!securityTextClean) {
3791
4174
  problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
4175
+ } else if (!securityPolicyClean) {
4176
+ problems.push(`security-review verdict says PASS but carries a finding (${securityPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's security policy threshold (${policy.securityThreshold}) — a reviewer's own PASS never overrides policy`);
3792
4177
  } else if (!securityBound) {
3793
4178
  problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
4179
+ } else if (!securityBinding.base) {
4180
+ problems.push(`the newest security-review verdict was cast against base ${security.baseSha ?? "no recorded base"}, the PR's base is now ${currentManifest.baseSha ?? "unresolved"} (a base-only change under an unchanged candidate — same patch text on a new base is not automatically equivalent)`);
3794
4181
  } else if (!securityObjectivesBound) {
3795
4182
  problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
3796
4183
  } else if (!securityRulingsBound) {
3797
4184
  problems.push(`the newest security-review verdict was cast against ruling ordinal ${security.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
4185
+ } else if (!securityBinding.briefHash) {
4186
+ problems.push(`the newest security-review verdict was cast against brief hash ${security.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
4187
+ } else if (!securityBinding.policyDigest) {
4188
+ problems.push(`the newest security-review verdict was cast against review policy digest ${security.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
3798
4189
  }
3799
4190
  if (!mechanicalChecksClean) {
3800
4191
  problems.push(reportedMechanicalChecks.length === 0 ? "no mechanical checks have reported for this head yet" : `mechanical check(s) not green: ${reportedMechanicalChecks.filter((c) => c.bucket !== "pass").map((c) => `${c.name} (${c.bucket})`).join(", ")}`);
@@ -4486,42 +4877,45 @@ function checkDispatchReadiness(input) {
4486
4877
  const { trancheSlug, task } = input;
4487
4878
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
4488
4879
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
4489
- const blockers = [];
4880
+ const blockerDetails = [];
4881
+ const push = (blockerClass, message) => {
4882
+ blockerDetails.push({ class: blockerClass, message });
4883
+ };
4490
4884
  if (task.issue === null) {
4491
- blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
4885
+ push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
4492
4886
  } else if (input.issue === null) {
4493
- blockers.push(`dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
4887
+ push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
4494
4888
  }
4495
4889
  if (input.issue !== null && !input.issueRationalePass) {
4496
- blockers.push(`dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
4890
+ push("rationale", `dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
4497
4891
  }
4498
4892
  for (const dep of input.dependsOn) {
4499
4893
  if (isSelfDependency(dep, task, input.issue)) {
4500
4894
  const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
4501
- blockers.push(`dispatch-gate INTERNAL: parsed a self-dependency for task ${task.id} (${issueStr}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream. Re-run once the rationale is corrected or the fix ships.`);
4895
+ push("internal-self-dependency", `dispatch-gate INTERNAL: parsed a self-dependency for task ${task.id} (${issueStr}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream. Re-run once the rationale is corrected or the fix ships.`);
4502
4896
  continue;
4503
4897
  }
4504
4898
  if (dep.resolved === false) {
4505
- blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on "${dep.id}", which is UNRESOLVABLE — the resolver could not find a matching tranche/task/Issue for this edge (not a claim about merge status). Not dispatchable until the edge is corrected.`);
4899
+ push("depends-on-unresolvable", `dispatch-gate depends-on: ${taskLabel} depends on "${dep.id}", which is UNRESOLVABLE — the resolver could not find a matching tranche/task/Issue for this edge (not a claim about merge status). Not dispatchable until the edge is corrected.`);
4506
4900
  continue;
4507
4901
  }
4508
4902
  if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
4509
4903
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
4510
- blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
4904
+ push("depends-on-not-merged", `dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
4511
4905
  }
4512
4906
  }
4513
4907
  for (const c of input.conflictsWith) {
4514
4908
  if (c.openOrInFlight) {
4515
4909
  const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
4516
- blockers.push(`dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
4910
+ push("conflicts-with", `dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
4517
4911
  }
4518
4912
  }
4519
4913
  for (const proj of input.priorTrancheArchival) {
4520
4914
  if (proj.priorTrancheSlug !== null && !proj.archived) {
4521
- blockers.push(`dispatch-gate prior-tranche-archival: project \`${proj.project}\`'s previous tranche \`${proj.priorTrancheSlug}\` is not archived — the Tranche Archivist must run before new work on this product.`);
4915
+ push("prior-tranche-archival", `dispatch-gate prior-tranche-archival: project \`${proj.project}\`'s previous tranche \`${proj.priorTrancheSlug}\` is not archived — the Tranche Archivist must run before new work on this product.`);
4522
4916
  }
4523
4917
  }
4524
- return { ready: blockers.length === 0, blockers };
4918
+ return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
4525
4919
  }
4526
4920
  // ../../packages/aeg-core/src/single-plan-pr.ts
4527
4921
  function trancheSlugFromTopologyPath(path) {
@@ -4623,6 +5017,137 @@ function checkBranchTopology(input) {
4623
5017
  reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
4624
5018
  };
4625
5019
  }
5020
+ // ../../packages/aeg-core/src/task-tools.ts
5021
+ import { z } from "zod";
5022
+ var TASK_TOOL_ERROR_KINDS = [
5023
+ "validation",
5024
+ "authority",
5025
+ "precondition",
5026
+ "capability",
5027
+ "infrastructure",
5028
+ "cancellation",
5029
+ "timeout",
5030
+ "uncertain_effect"
5031
+ ];
5032
+ var TaskToolErrorSchema = z.object({
5033
+ kind: z.enum(TASK_TOOL_ERROR_KINDS),
5034
+ message: z.string().min(1),
5035
+ detail: z.string().optional()
5036
+ });
5037
+ var TaskToolRefSchema = z.union([
5038
+ z.object({ tranche: z.string().min(1), id: z.string().min(1) }).strict(),
5039
+ z.object({ issue: z.number().int().positive() }).strict()
5040
+ ]);
5041
+ var MAX_PAGE_LIMIT = 100;
5042
+ var PageRequestSchema = z.object({
5043
+ cursor: z.string().optional(),
5044
+ limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
5045
+ });
5046
+ var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
5047
+ var ObservedSchema = z.object({
5048
+ observedAt: z.string(),
5049
+ freshness: FreshnessSchema
5050
+ });
5051
+ var TaskStatusInputSchema = z.object({
5052
+ task: TaskToolRefSchema.optional()
5053
+ }).merge(PageRequestSchema).strict();
5054
+ var TaskStatusItemSchema = z.object({
5055
+ task: TaskToolRefSchema,
5056
+ issue: z.number().int().positive(),
5057
+ pr: z.number().int().positive().nullable(),
5058
+ state: z.string()
5059
+ }).merge(ObservedSchema);
5060
+ var TaskStatusResultSchema = z.object({
5061
+ items: z.array(TaskStatusItemSchema),
5062
+ nextCursor: z.string().nullable()
5063
+ });
5064
+ var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
5065
+ var EscalationInputsSchema = z.object({
5066
+ task: z.number().int().positive(),
5067
+ round: z.number().int().nonnegative(),
5068
+ head: z.string(),
5069
+ branch: z.string(),
5070
+ prNumber: z.number().int().positive()
5071
+ });
5072
+ var EscalationEvidenceSchema = z.object({
5073
+ round: z.number().int().nonnegative(),
5074
+ reviewer: z.string().nullable(),
5075
+ security: z.string().nullable()
5076
+ });
5077
+ var EscalationRunIdentitySchema = z.object({
5078
+ runId: z.string(),
5079
+ pid: z.number().int().positive(),
5080
+ host: z.string()
5081
+ });
5082
+ var EscalationInputVersionsSchema = z.object({
5083
+ briefHash: z.string().nullable(),
5084
+ objectivesVersion: z.string().nullable(),
5085
+ rulingOrdinal: z.number().int().nonnegative(),
5086
+ policyDigest: z.string()
5087
+ });
5088
+ var TaskEscalationReadInputSchema = z.object({
5089
+ task: TaskToolRefSchema
5090
+ }).merge(PageRequestSchema).strict();
5091
+ var TaskEscalationPacketSchema = z.object({
5092
+ reason: z.string(),
5093
+ detail: z.string().nullable(),
5094
+ inputs: EscalationInputsSchema.nullable(),
5095
+ evidence: EscalationEvidenceSchema.nullable(),
5096
+ attemptedRecovery: z.string(),
5097
+ requestedAuthority: RequestedAuthoritySchema,
5098
+ permittedNextActions: z.array(z.string()),
5099
+ runIdentity: EscalationRunIdentitySchema.nullable(),
5100
+ inputVersions: EscalationInputVersionsSchema.nullable()
5101
+ }).merge(ObservedSchema);
5102
+ var TaskEscalationReadResultSchema = z.object({
5103
+ items: z.array(TaskEscalationPacketSchema),
5104
+ nextCursor: z.string().nullable()
5105
+ }).merge(ObservedSchema);
5106
+ var TaskStartInputSchema = z.object({
5107
+ tranche: z.string().min(1),
5108
+ id: z.string().min(1)
5109
+ }).strict();
5110
+ var TaskStartResultSchema = z.object({
5111
+ requestId: z.string().min(1),
5112
+ run: z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
5113
+ started: z.boolean(),
5114
+ startedAt: z.string(),
5115
+ mode: z.literal("attended")
5116
+ });
5117
+ var TaskResumeInputSchema = z.object({
5118
+ task: TaskToolRefSchema
5119
+ }).strict();
5120
+ var TaskCancelInputSchema = z.object({
5121
+ task: TaskToolRefSchema,
5122
+ reason: z.string().min(1)
5123
+ }).strict();
5124
+ var TaskResumeResultSchema = z.object({
5125
+ task: z.number().int().positive(),
5126
+ pr: z.number().int().positive(),
5127
+ escalationId: z.string().min(1),
5128
+ outcome: z.enum(["started", "already_resumed"]),
5129
+ authenticatedBy: z.string().min(1),
5130
+ authenticatedFrom: z.string().min(1)
5131
+ });
5132
+ var TaskCancelOutcomeSchema = z.enum(["confirmed", "pending", "uncertain"]);
5133
+ var TaskCancelResultSchema = z.object({
5134
+ task: z.number().int().positive(),
5135
+ pr: z.number().int().positive(),
5136
+ escalationId: z.string().min(1),
5137
+ outcome: TaskCancelOutcomeSchema,
5138
+ authenticatedBy: z.string().min(1),
5139
+ authenticatedFrom: z.string().min(1),
5140
+ fencedEffectKeys: z.array(z.string())
5141
+ });
5142
+ var TASK_TOOL_NAMES = [
5143
+ "task_start",
5144
+ "task_status",
5145
+ "task_escalation_read",
5146
+ "task_resume",
5147
+ "task_cancel"
5148
+ ];
5149
+ var OPERATOR_STATUS_FOLLOW = "task_status_follow";
5150
+ var OPERATOR_TOOL_GRANT = [...TASK_TOOL_NAMES, OPERATOR_STATUS_FOLLOW];
4626
5151
  // ../../packages/aeg-core/src/first-push-dispatch-gate.ts
4627
5152
  function parseTaskBranch(branch) {
4628
5153
  const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
@@ -4706,7 +5231,6 @@ function decideIssueAssignment(input) {
4706
5231
  };
4707
5232
  }
4708
5233
  // ../../packages/aeg-core/src/test-plan-gate.ts
4709
- var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
4710
5234
  function evaluateTestPlanGate(body, branch) {
4711
5235
  if (!body) {
4712
5236
  return {
@@ -4719,7 +5243,7 @@ function evaluateTestPlanGate(body, branch) {
4719
5243
  }
4720
5244
  const located = locateTestPlanSection(body);
4721
5245
  if (!located.found) {
4722
- if (TASK_BRANCH_PATTERN2.test(branch)) {
5246
+ if (parseTaskBranchIdentity(branch) !== null) {
4723
5247
  return {
4724
5248
  verdict: "fail",
4725
5249
  messages: [
@@ -4846,8 +5370,11 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
4846
5370
  }
4847
5371
  return findings;
4848
5372
  }
5373
+ // ../../packages/aeg-core/src/log/envelope.ts
5374
+ import { createHash as createHash5 } from "node:crypto";
5375
+
4849
5376
  // ../../packages/aeg-core/src/log/schema.ts
4850
- import { z } from "zod";
5377
+ import { z as z2 } from "zod";
4851
5378
  var ROLE_VALUES = [
4852
5379
  "planner",
4853
5380
  "developer",
@@ -4857,136 +5384,174 @@ var ROLE_VALUES = [
4857
5384
  "archivist",
4858
5385
  "architect"
4859
5386
  ];
4860
- var RoleSchema = z.enum(ROLE_VALUES);
5387
+ var RoleSchema = z2.enum(ROLE_VALUES);
4861
5388
  var HOST_VALUES = ["hook", "ci", "cli", "loop"];
4862
- var HostSchema = z.enum(HOST_VALUES);
5389
+ var HostSchema = z2.enum(HOST_VALUES);
4863
5390
  var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
4864
- var HeaderMetaSchema = z.object({
4865
- schema: z.literal(1),
4866
- ts: z.string(),
4867
- run_id: z.string().regex(RUN_ID_PATTERN),
4868
- seq: z.number().int().nonnegative(),
4869
- repo: z.string().nullable(),
4870
- vinaya: z.string(),
4871
- doctrine: z.string(),
5391
+ var headerMetaCore = {
5392
+ ts: z2.string(),
5393
+ run_id: z2.string().regex(RUN_ID_PATTERN),
5394
+ seq: z2.number().int().nonnegative(),
5395
+ repo: z2.string().nullable(),
5396
+ vinaya: z2.string(),
5397
+ doctrine: z2.string(),
4872
5398
  host: HostSchema,
4873
- machine: z.string()
5399
+ machine: z2.string()
5400
+ };
5401
+ var HeaderMetaV1Schema = z2.object({
5402
+ schema: z2.literal(1),
5403
+ ...headerMetaCore
5404
+ }).strict();
5405
+ var LineageSchema = z2.object({
5406
+ run: z2.string().nullable(),
5407
+ attempt: z2.number().int().nullable(),
5408
+ parent: z2.string().nullable()
5409
+ }).strict();
5410
+ var InputVersionsSchema = z2.object({
5411
+ objectives_version: z2.string().nullable(),
5412
+ brief_hash: z2.string().nullable(),
5413
+ ruling_ordinal: z2.number().int().nullable(),
5414
+ policy_digest: z2.string().nullable()
4874
5415
  }).strict();
4875
- var SubjectSchema = z.object({
4876
- issue: z.number().int().nullable(),
4877
- pr: z.number().int().optional(),
4878
- sha: z.string().optional(),
4879
- role: z.union([RoleSchema, z.literal("unattributed")]),
4880
- round: z.number().int().optional(),
4881
- objectives_version: z.string().optional()
5416
+ var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
5417
+ var HeaderMetaV2Schema = z2.object({
5418
+ schema: z2.literal(2),
5419
+ ...headerMetaCore,
5420
+ event_id: z2.string().min(1),
5421
+ process_id: z2.string().min(1),
5422
+ actor_id: z2.string().nullable(),
5423
+ lineage: LineageSchema,
5424
+ input_versions: InputVersionsSchema,
5425
+ provenance: ProvenanceSchema
4882
5426
  }).strict();
4883
- var HeaderSchema = z.object({
5427
+ var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
5428
+ var SubjectSchema = z2.object({
5429
+ issue: z2.number().int().nullable(),
5430
+ pr: z2.number().int().optional(),
5431
+ sha: z2.string().optional(),
5432
+ role: z2.union([RoleSchema, z2.literal("unattributed")]),
5433
+ round: z2.number().int().optional(),
5434
+ objectives_version: z2.string().optional()
5435
+ }).strict();
5436
+ var HeaderSchema = z2.object({
4884
5437
  meta: HeaderMetaSchema,
4885
5438
  subject: SubjectSchema
4886
5439
  }).strict();
4887
5440
  var envelopeTail = {
4888
- duration_ms: z.number().nonnegative().optional(),
4889
- payload: z.object({}).strict()
5441
+ duration_ms: z2.number().nonnegative().optional(),
5442
+ payload: z2.object({}).strict()
4890
5443
  };
4891
- var DispatchOutcomeSchema = z.discriminatedUnion("type", [
4892
- z.object({ type: z.literal("pr_opened"), pr: z.number().int(), head: z.string() }).strict(),
4893
- z.object({
4894
- type: z.literal("round_pushed"),
4895
- pr: z.number().int(),
4896
- head: z.string(),
4897
- comment_id: z.number().int()
5444
+ var ReviewFindingSchema = z2.object({
5445
+ id: z2.string(),
5446
+ severity: z2.string(),
5447
+ state: z2.string().optional(),
5448
+ severity_scale: z2.string().optional(),
5449
+ policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
5450
+ confidence: z2.number().min(0).max(1).optional(),
5451
+ confidence_scale: z2.string().optional(),
5452
+ confidence_source: z2.string().optional()
5453
+ }).strict();
5454
+ var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
5455
+ z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
5456
+ z2.object({
5457
+ type: z2.literal("round_pushed"),
5458
+ pr: z2.number().int(),
5459
+ head: z2.string(),
5460
+ comment_id: z2.number().int()
4898
5461
  }).strict(),
4899
- z.object({
4900
- type: z.literal("verdict"),
4901
- verdict: z.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
4902
- head: z.string(),
4903
- comment_id: z.number().int(),
4904
- objectives: z.array(z.object({ id: z.string(), met: z.boolean() }).strict()),
4905
- findings: z.array(z.object({ id: z.string(), severity: z.string(), state: z.string().optional() }).strict())
5462
+ z2.object({
5463
+ type: z2.literal("verdict"),
5464
+ verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
5465
+ head: z2.string(),
5466
+ comment_id: z2.number().int(),
5467
+ objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
5468
+ findings: z2.array(ReviewFindingSchema)
4906
5469
  }).strict(),
4907
- z.object({
4908
- type: z.literal("escalation"),
4909
- class: z.enum(["authority", "strategy", "product"]),
4910
- comment_id: z.number().int()
5470
+ z2.object({
5471
+ type: z2.literal("escalation"),
5472
+ class: z2.enum(["authority", "strategy", "product"]),
5473
+ comment_id: z2.number().int()
4911
5474
  }).strict(),
4912
- z.object({ type: z.literal("brief"), comment_id: z.number().int(), hash: z.string() }).strict(),
4913
- z.object({ type: z.literal("plan"), issues: z.array(z.number().int()) }).strict(),
4914
- z.object({ type: z.literal("archive"), provenance_comment_id: z.number().int() }).strict()
5475
+ z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
5476
+ z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
5477
+ z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict(),
5478
+ z2.object({ type: z2.literal("completed") }).strict()
4915
5479
  ]);
4916
5480
  var dispatchShared = {
4917
5481
  meta: HeaderMetaSchema,
4918
5482
  subject: SubjectSchema,
4919
- kind: z.literal("dispatch"),
5483
+ kind: z2.literal("dispatch"),
4920
5484
  ...envelopeTail,
4921
5485
  target_role: RoleSchema,
4922
- model: z.string(),
4923
- round: z.number().int().optional(),
4924
- effect_id: z.string()
5486
+ model: z2.string(),
5487
+ round: z2.number().int().optional(),
5488
+ effect_id: z2.string()
4925
5489
  };
4926
- var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
4927
- var DispatchEventSchema = z.discriminatedUnion("event", [
4928
- z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
4929
- z.object({
5490
+ var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
5491
+ var DispatchEventSchema = z2.discriminatedUnion("event", [
5492
+ z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
5493
+ z2.object({
4930
5494
  ...dispatchShared,
4931
- event: z.literal("outcome_received"),
5495
+ event: z2.literal("outcome_received"),
4932
5496
  outcome: DispatchOutcomeSchema,
4933
5497
  usage: dispatchUsageField
4934
5498
  }).strict(),
4935
- z.object({
5499
+ z2.object({
4936
5500
  ...dispatchShared,
4937
- event: z.literal("dispatch_failed"),
4938
- reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
5501
+ event: z2.literal("dispatch_failed"),
5502
+ reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
4939
5503
  usage: dispatchUsageField
4940
5504
  }).strict()
4941
5505
  ]);
4942
5506
  var loopShared = {
4943
5507
  meta: HeaderMetaSchema,
4944
5508
  subject: SubjectSchema,
4945
- kind: z.literal("dev_review_loop"),
5509
+ kind: z2.literal("dev_review_loop"),
4946
5510
  ...envelopeTail,
4947
- loop_id: z.string()
5511
+ loop_id: z2.string()
4948
5512
  };
4949
- var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
4950
- z.object({
5513
+ var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
5514
+ z2.object({
4951
5515
  ...loopShared,
4952
- event: z.literal("loop_started"),
4953
- task: z.number().int(),
4954
- policy: z.object({
4955
- max_rounds: z.number().int().nonnegative(),
4956
- reviewers: z.array(RoleSchema),
4957
- models: z.record(RoleSchema, z.string())
5516
+ event: z2.literal("loop_started"),
5517
+ task: z2.number().int(),
5518
+ policy: z2.object({
5519
+ max_rounds: z2.number().int().nonnegative(),
5520
+ reviewers: z2.array(RoleSchema),
5521
+ models: z2.record(RoleSchema, z2.string())
4958
5522
  }).strict()
4959
5523
  }).strict(),
4960
- z.object({ ...loopShared, event: z.literal("round_started"), round: z.number().int(), base_head: z.string() }).strict(),
4961
- z.object({
5524
+ z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
5525
+ z2.object({
4962
5526
  ...loopShared,
4963
- event: z.literal("gate_result_read"),
4964
- round: z.number().int(),
4965
- head: z.string(),
4966
- green: z.boolean()
5527
+ event: z2.literal("gate_result_read"),
5528
+ round: z2.number().int(),
5529
+ head: z2.string(),
5530
+ green: z2.boolean()
4967
5531
  }).strict(),
4968
- z.object({
5532
+ z2.object({
4969
5533
  ...loopShared,
4970
- event: z.literal("verdicts_read"),
4971
- round: z.number().int(),
4972
- head: z.string(),
4973
- all_approve: z.boolean(),
4974
- blockers: z.number().int().nonnegative()
5534
+ event: z2.literal("verdicts_read"),
5535
+ round: z2.number().int(),
5536
+ head: z2.string(),
5537
+ all_approve: z2.boolean(),
5538
+ blockers: z2.number().int().nonnegative(),
5539
+ findings: z2.array(ReviewFindingSchema).optional()
4975
5540
  }).strict(),
4976
- z.object({
5541
+ z2.object({
4977
5542
  ...loopShared,
4978
- event: z.literal("findings_compared"),
4979
- round: z.number().int(),
4980
- open: z.array(z.string()),
4981
- resolved: z.array(z.string()),
4982
- new: z.array(z.string()),
4983
- recurring: z.array(z.string())
5543
+ event: z2.literal("findings_compared"),
5544
+ round: z2.number().int(),
5545
+ open: z2.array(z2.string()),
5546
+ resolved: z2.array(z2.string()),
5547
+ new: z2.array(z2.string()),
5548
+ recurring: z2.array(z2.string())
4984
5549
  }).strict(),
4985
- z.object({
5550
+ z2.object({
4986
5551
  ...loopShared,
4987
- event: z.literal("stop_condition_met"),
4988
- round: z.number().int(),
4989
- condition: z.enum([
5552
+ event: z2.literal("stop_condition_met"),
5553
+ round: z2.number().int(),
5554
+ condition: z2.enum([
4990
5555
  "green",
4991
5556
  "max_rounds",
4992
5557
  "no_progress",
@@ -4996,42 +5561,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
4996
5561
  "reappearance"
4997
5562
  ])
4998
5563
  }).strict(),
4999
- z.object({
5564
+ z2.object({
5565
+ ...loopShared,
5566
+ event: z2.literal("paused"),
5567
+ round: z2.number().int(),
5568
+ reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
5569
+ }).strict(),
5570
+ z2.object({
5000
5571
  ...loopShared,
5001
- event: z.literal("paused"),
5002
- round: z.number().int(),
5003
- reason: z.enum(["escalation", "principal_item", "refreeze_needed"])
5572
+ event: z2.literal("resumed"),
5573
+ round: z2.number().int(),
5574
+ by: z2.literal("principal")
5004
5575
  }).strict(),
5005
- z.object({
5576
+ z2.object({
5006
5577
  ...loopShared,
5007
- event: z.literal("resumed"),
5008
- round: z.number().int(),
5009
- by: z.literal("principal")
5578
+ event: z2.literal("unpushed_work_resume"),
5579
+ round: z2.number().int(),
5580
+ branch: z2.string(),
5581
+ detail: z2.string()
5010
5582
  }).strict(),
5011
- z.object({
5583
+ z2.object({
5012
5584
  ...loopShared,
5013
- event: z.literal("round_ended"),
5014
- round: z.number().int(),
5015
- base_head: z.string(),
5016
- head: z.string(),
5017
- files_changed: z.number().int().nonnegative(),
5018
- insertions: z.number().int().nonnegative(),
5019
- deletions: z.number().int().nonnegative(),
5020
- wall_ms: z.number().nonnegative(),
5021
- outcome: z.enum(["green", "changes_requested", "escalated"])
5585
+ event: z2.literal("round_ended"),
5586
+ round: z2.number().int(),
5587
+ base_head: z2.string(),
5588
+ head: z2.string(),
5589
+ files_changed: z2.number().int().nonnegative(),
5590
+ insertions: z2.number().int().nonnegative(),
5591
+ deletions: z2.number().int().nonnegative(),
5592
+ wall_ms: z2.number().nonnegative(),
5593
+ outcome: z2.enum(["green", "changes_requested", "escalated"])
5022
5594
  }).strict(),
5023
- z.object({
5595
+ z2.object({
5024
5596
  ...loopShared,
5025
- event: z.literal("journal_finalized"),
5026
- rounds: z.number().int().nonnegative(),
5027
- total_wall_ms: z.number().nonnegative(),
5028
- time_to_green_ms: z.number().nonnegative().nullable(),
5029
- files_changed_total: z.number().int().nonnegative(),
5030
- final_head: z.string(),
5031
- result: z.enum(["merged_ready", "stopped"])
5597
+ event: z2.literal("journal_finalized"),
5598
+ rounds: z2.number().int().nonnegative(),
5599
+ total_wall_ms: z2.number().nonnegative(),
5600
+ time_to_green_ms: z2.number().nonnegative().nullable(),
5601
+ files_changed_total: z2.number().int().nonnegative(),
5602
+ final_head: z2.string(),
5603
+ result: z2.enum(["merged_ready", "stopped"])
5032
5604
  }).strict()
5033
5605
  ]);
5034
- var ForgeOpSchema = z.enum([
5606
+ var ForgeOpSchema = z2.enum([
5035
5607
  "pr.create",
5036
5608
  "pr.comment",
5037
5609
  "pr.body.replace",
@@ -5045,24 +5617,434 @@ var ForgeOpSchema = z.enum([
5045
5617
  "label.add",
5046
5618
  "label.remove"
5047
5619
  ]);
5048
- var ForgeWriteTargetSchema = z.object({
5049
- issue: z.number().int().optional(),
5050
- pr: z.number().int().optional()
5620
+ var ForgeWriteTargetSchema = z2.object({
5621
+ issue: z2.number().int().optional(),
5622
+ pr: z2.number().int().optional()
5051
5623
  }).strict();
5052
5624
  var forgeWriteShared = {
5053
5625
  meta: HeaderMetaSchema,
5054
5626
  subject: SubjectSchema,
5055
- kind: z.literal("forge_write"),
5627
+ kind: z2.literal("forge_write"),
5056
5628
  ...envelopeTail,
5057
5629
  op: ForgeOpSchema,
5058
5630
  target: ForgeWriteTargetSchema
5059
5631
  };
5060
- var ForgeWriteEventSchema = z.discriminatedUnion("event", [
5061
- z.object({ ...forgeWriteShared, event: z.literal("validated") }).strict(),
5062
- z.object({ ...forgeWriteShared, event: z.literal("refused"), reason: z.string() }).strict(),
5063
- z.object({ ...forgeWriteShared, event: z.literal("written"), comment_ids: z.array(z.string()) }).strict()
5632
+ var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
5633
+ z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
5634
+ z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
5635
+ z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
5636
+ ]);
5637
+ var GateOutcomeSchema = z2.enum([
5638
+ "pass",
5639
+ "fail",
5640
+ "wait",
5641
+ "skip",
5642
+ "invalid_input",
5643
+ "unavailable_dependency",
5644
+ "timeout",
5645
+ "cancelled"
5646
+ ]);
5647
+ var gateShared = {
5648
+ meta: HeaderMetaSchema,
5649
+ subject: SubjectSchema,
5650
+ kind: z2.literal("gate"),
5651
+ ...envelopeTail,
5652
+ check: z2.string(),
5653
+ check_version: z2.string().nullable(),
5654
+ policy_version: z2.string().nullable(),
5655
+ input_fingerprint: z2.string().nullable()
5656
+ };
5657
+ var GateEventSchema = z2.discriminatedUnion("event", [
5658
+ z2.object({
5659
+ ...gateShared,
5660
+ event: z2.literal("checked"),
5661
+ outcome: GateOutcomeSchema,
5662
+ reason: z2.string().optional()
5663
+ }).strict()
5664
+ ]);
5665
+ var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
5666
+ var operationShared = {
5667
+ meta: HeaderMetaSchema,
5668
+ subject: SubjectSchema,
5669
+ kind: z2.literal("operation"),
5670
+ ...envelopeTail,
5671
+ operation: z2.string(),
5672
+ target: z2.string().nullable()
5673
+ };
5674
+ var OperationEventSchema = z2.discriminatedUnion("event", [
5675
+ z2.object({
5676
+ ...operationShared,
5677
+ event: z2.literal("completed"),
5678
+ result: OperationResultSchema,
5679
+ error_class: z2.string().nullable()
5680
+ }).strict()
5064
5681
  ]);
5065
- var LogEventSchema = z.union([DispatchEventSchema, DevReviewLoopEventSchema, ForgeWriteEventSchema]);
5682
+ var UsageUnitsSchema = z2.object({
5683
+ input: z2.number().nonnegative().nullable(),
5684
+ output: z2.number().nonnegative().nullable(),
5685
+ cache: z2.number().nonnegative().nullable()
5686
+ }).strict();
5687
+ var usageShared = {
5688
+ meta: HeaderMetaSchema,
5689
+ subject: SubjectSchema,
5690
+ kind: z2.literal("usage"),
5691
+ ...envelopeTail,
5692
+ model: z2.string().nullable(),
5693
+ source: z2.string(),
5694
+ semantics: z2.enum(["cumulative", "delta"])
5695
+ };
5696
+ var UsageEventSchema = z2.discriminatedUnion("event", [
5697
+ z2.object({
5698
+ ...usageShared,
5699
+ event: z2.literal("observed"),
5700
+ units: UsageUnitsSchema,
5701
+ unknown_reason: z2.string().nullable()
5702
+ }).strict()
5703
+ ]);
5704
+ var RoleAttemptOutcomeSchema = z2.enum([
5705
+ "completed",
5706
+ "incomplete",
5707
+ "infrastructure_failed",
5708
+ "cancelled",
5709
+ "timed_out",
5710
+ "capability_refused"
5711
+ ]);
5712
+ var roleAttemptShared = {
5713
+ meta: HeaderMetaSchema,
5714
+ subject: SubjectSchema,
5715
+ kind: z2.literal("role_attempt"),
5716
+ ...envelopeTail,
5717
+ actor: z2.string().nullable(),
5718
+ attempt: z2.number().int().nullable(),
5719
+ effect_id: z2.string(),
5720
+ model: z2.string().nullable()
5721
+ };
5722
+ var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
5723
+ z2.object({
5724
+ ...roleAttemptShared,
5725
+ event: z2.literal("attempted"),
5726
+ outcome: RoleAttemptOutcomeSchema,
5727
+ usage: dispatchUsageField
5728
+ }).strict()
5729
+ ]);
5730
+ var handoffShared = {
5731
+ meta: HeaderMetaSchema,
5732
+ subject: SubjectSchema,
5733
+ kind: z2.literal("handoff"),
5734
+ ...envelopeTail,
5735
+ class: z2.enum(["authority", "strategy", "product"]),
5736
+ reason: z2.string()
5737
+ };
5738
+ var HandoffEventSchema = z2.discriminatedUnion("event", [
5739
+ z2.object({
5740
+ ...handoffShared,
5741
+ event: z2.literal("raised"),
5742
+ requested_decision: z2.string().nullable()
5743
+ }).strict(),
5744
+ z2.object({
5745
+ ...handoffShared,
5746
+ event: z2.literal("resolved"),
5747
+ resolution: z2.string().nullable(),
5748
+ resolved_by: z2.string().nullable()
5749
+ }).strict()
5750
+ ]);
5751
+ var EffectTargetSchema = z2.object({
5752
+ kind: z2.string(),
5753
+ ref: z2.string()
5754
+ }).strict();
5755
+ var effectShared = {
5756
+ meta: HeaderMetaSchema,
5757
+ subject: SubjectSchema,
5758
+ kind: z2.literal("effect"),
5759
+ ...envelopeTail,
5760
+ effect_id: z2.string(),
5761
+ target: EffectTargetSchema
5762
+ };
5763
+ var EffectEventSchema = z2.discriminatedUnion("event", [
5764
+ z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
5765
+ z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
5766
+ z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
5767
+ ]);
5768
+ var LogEventSchema = z2.union([
5769
+ DispatchEventSchema,
5770
+ DevReviewLoopEventSchema,
5771
+ ForgeWriteEventSchema,
5772
+ GateEventSchema,
5773
+ OperationEventSchema,
5774
+ UsageEventSchema,
5775
+ RoleAttemptEventSchema,
5776
+ HandoffEventSchema,
5777
+ EffectEventSchema
5778
+ ]);
5779
+
5780
+ // ../../packages/aeg-core/src/log/envelope.ts
5781
+ function isRole(value) {
5782
+ return value !== undefined && ROLE_VALUES.includes(value);
5783
+ }
5784
+ function actorIdFromEnv(role) {
5785
+ return role === undefined || role === "" ? null : role;
5786
+ }
5787
+ function lineageFieldFromEnv(value) {
5788
+ return value === undefined || value === "" ? null : value;
5789
+ }
5790
+ function attemptFromEnv(value) {
5791
+ if (value === undefined || value === "")
5792
+ return null;
5793
+ const n = Number(value);
5794
+ return Number.isInteger(n) ? n : null;
5795
+ }
5796
+ function provenanceFor(input) {
5797
+ if (input.provenance)
5798
+ return input.provenance;
5799
+ if (input.env.role || input.env.task)
5800
+ return "env_correlated";
5801
+ return "unavailable";
5802
+ }
5803
+ function issueFromTask(task) {
5804
+ if (task === undefined || task === "")
5805
+ return null;
5806
+ const n = Number(task);
5807
+ return Number.isInteger(n) ? n : null;
5808
+ }
5809
+ function roundFromEnv(round) {
5810
+ if (round === undefined || round === "")
5811
+ return;
5812
+ const n = Number(round);
5813
+ return Number.isInteger(n) ? n : undefined;
5814
+ }
5815
+ function buildHeader(input) {
5816
+ const role = isRole(input.env.role) ? input.env.role : "unattributed";
5817
+ const round = roundFromEnv(input.env.round);
5818
+ const subject = {
5819
+ issue: issueFromTask(input.env.task),
5820
+ role,
5821
+ ...round !== undefined ? { round } : {},
5822
+ ...input.subject ?? {}
5823
+ };
5824
+ return {
5825
+ meta: {
5826
+ schema: 2,
5827
+ ts: input.now.toISOString(),
5828
+ run_id: input.runId,
5829
+ seq: input.seq,
5830
+ repo: input.repo,
5831
+ vinaya: input.vinaya,
5832
+ doctrine: input.doctrine,
5833
+ host: input.host,
5834
+ machine: createHash5("sha256").update(input.hostname).digest("hex"),
5835
+ event_id: input.eventId,
5836
+ process_id: input.processId,
5837
+ actor_id: actorIdFromEnv(input.env.role),
5838
+ lineage: {
5839
+ run: lineageFieldFromEnv(input.env.run),
5840
+ attempt: attemptFromEnv(input.env.attempt),
5841
+ parent: lineageFieldFromEnv(input.env.parent)
5842
+ },
5843
+ input_versions: {
5844
+ objectives_version: input.inputVersions?.objectivesVersion ?? null,
5845
+ brief_hash: input.inputVersions?.briefHash ?? null,
5846
+ ruling_ordinal: input.inputVersions?.rulingOrdinal ?? null,
5847
+ policy_digest: input.inputVersions?.policyDigest ?? null
5848
+ },
5849
+ provenance: provenanceFor(input)
5850
+ },
5851
+ subject
5852
+ };
5853
+ }
5854
+ // ../../packages/aeg-core/src/log/redact.ts
5855
+ var GITHUB_TOKEN_PATTERN = /\b(?:gho_|ghp_|ghs_|ghu_|ghr_|github_pat_)[A-Za-z0-9_]+\b/g;
5856
+ var CLASSIC_GITHUB_PAT_PATTERN = /\b((?:token|GITHUB_TOKEN|PAT|api[_-]?key|api[_-]?token)\s*[:=]\s*)[a-f0-9]{40}\b/gi;
5857
+ var BEARER_TOKEN_PATTERN = /\bBearer\s+\S+/g;
5858
+ var AWS_ACCESS_KEY_PATTERN = /\bAKIA[0-9A-Z]{16}\b/g;
5859
+ var SLACK_TOKEN_PATTERN = /\bxox[baprs]-[0-9A-Za-z-]+\b/g;
5860
+ var STRIPE_KEY_PATTERN = /\b(?:sk|pk|rk)_(?:live|test)_[0-9A-Za-z]{10,}\b/g;
5861
+ var ANTHROPIC_KEY_PATTERN = /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g;
5862
+ var OPENAI_KEY_PATTERN = /\bsk-(?!ant-)[A-Za-z0-9]{20,}\b/g;
5863
+ var NPM_TOKEN_PATTERN = /\bnpm_[A-Za-z0-9]{36}\b/g;
5864
+ var JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
5865
+ var PEM_BLOCK_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
5866
+ var BASIC_AUTH_URL_PATTERN = /(https?:\/\/)[^/\s:@]+:[^/\s@]+@/g;
5867
+ var GENERIC_SECRET_ASSIGNMENT_PATTERN = /\b((?:secret|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[:=]\s*)[A-Za-z0-9+/_=-]{16,}\b/gi;
5868
+ function redactString(value, home) {
5869
+ let out = value.replace(GITHUB_TOKEN_PATTERN, "<redacted>").replace(CLASSIC_GITHUB_PAT_PATTERN, "$1<redacted>").replace(BEARER_TOKEN_PATTERN, "<redacted>").replace(AWS_ACCESS_KEY_PATTERN, "<redacted>").replace(SLACK_TOKEN_PATTERN, "<redacted>").replace(STRIPE_KEY_PATTERN, "<redacted>").replace(ANTHROPIC_KEY_PATTERN, "<redacted>").replace(OPENAI_KEY_PATTERN, "<redacted>").replace(NPM_TOKEN_PATTERN, "<redacted>").replace(JWT_PATTERN, "<redacted>").replace(PEM_BLOCK_PATTERN, "<redacted>").replace(BASIC_AUTH_URL_PATTERN, "$1<redacted>@").replace(GENERIC_SECRET_ASSIGNMENT_PATTERN, "$1<redacted>");
5870
+ if (home.length > 0 && out.startsWith(home)) {
5871
+ const rest = out.slice(home.length);
5872
+ if (rest === "" || rest.startsWith("/"))
5873
+ out = `~${rest}`;
5874
+ }
5875
+ return out;
5876
+ }
5877
+ function redact(value, home) {
5878
+ if (typeof value === "string")
5879
+ return redactString(value, home);
5880
+ if (Array.isArray(value))
5881
+ return value.map((v) => redact(v, home));
5882
+ if (value !== null && typeof value === "object") {
5883
+ const out = {};
5884
+ for (const [k, v] of Object.entries(value)) {
5885
+ out[k] = redact(v, home);
5886
+ }
5887
+ return out;
5888
+ }
5889
+ return value;
5890
+ }
5891
+ // ../../packages/aeg-core/src/log/store.ts
5892
+ function metaOf(obj) {
5893
+ if (obj === null || typeof obj !== "object")
5894
+ return null;
5895
+ const meta = obj.meta;
5896
+ if (meta === null || typeof meta !== "object")
5897
+ return null;
5898
+ return meta;
5899
+ }
5900
+ function recordIdentity(obj) {
5901
+ const meta = metaOf(obj);
5902
+ if (meta === null)
5903
+ return null;
5904
+ const eventId = meta.event_id;
5905
+ if (typeof eventId === "string" && eventId.length > 0)
5906
+ return eventId;
5907
+ const runId = meta.run_id;
5908
+ const seq = meta.seq;
5909
+ if (typeof runId === "string" && runId.length > 0 && typeof seq === "number" && Number.isInteger(seq)) {
5910
+ return `${runId}:${seq}`;
5911
+ }
5912
+ return null;
5913
+ }
5914
+
5915
+ // ../../packages/aeg-core/src/log/artifact.ts
5916
+ var TASK_LOG_ARTIFACT_MAX_BYTES = 8 * 1024 * 1024;
5917
+ // ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
5918
+ var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
5919
+ // ../../packages/aeg-core/src/control-store/records.ts
5920
+ import { z as z3 } from "zod";
5921
+ var isoTimestamp = z3.string().min(1);
5922
+ var taskId = z3.number().int().positive();
5923
+ var epochNumber = z3.number().int().nonnegative();
5924
+ var RunRecordSchema = z3.object({
5925
+ version: z3.literal(1),
5926
+ kind: z3.literal("run"),
5927
+ task: taskId,
5928
+ runId: z3.string().min(1),
5929
+ pid: z3.number().int().positive(),
5930
+ host: z3.string().min(1),
5931
+ startedAt: isoTimestamp,
5932
+ childStartedAt: z3.string().min(1).nullable().optional(),
5933
+ childCommand: z3.string().min(1).nullable().optional()
5934
+ }).strict();
5935
+ var InputRecordSchema = z3.object({
5936
+ version: z3.literal(1),
5937
+ kind: z3.literal("input"),
5938
+ task: taskId,
5939
+ runId: z3.string().min(1),
5940
+ source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
5941
+ pr: z3.number().int().positive().nullable(),
5942
+ round: z3.number().int().nonnegative(),
5943
+ recordedAt: isoTimestamp
5944
+ }).strict();
5945
+ var OwnershipRecordSchema = z3.object({
5946
+ version: z3.literal(1),
5947
+ kind: z3.literal("ownership"),
5948
+ task: taskId,
5949
+ epoch: epochNumber,
5950
+ ownerId: z3.string().min(1),
5951
+ pid: z3.number().int().positive(),
5952
+ host: z3.string().min(1),
5953
+ acquiredAt: isoTimestamp
5954
+ }).strict();
5955
+ var TransitionRecordSchema = z3.object({
5956
+ version: z3.literal(1),
5957
+ kind: z3.literal("transition"),
5958
+ task: taskId,
5959
+ epoch: epochNumber,
5960
+ seq: z3.number().int().nonnegative(),
5961
+ from: z3.string().min(1),
5962
+ to: z3.string().min(1),
5963
+ detail: z3.string().optional(),
5964
+ at: isoTimestamp
5965
+ }).strict();
5966
+ var ManifestRecordSchema = z3.object({
5967
+ version: z3.literal(1),
5968
+ kind: z3.literal("manifest"),
5969
+ task: taskId,
5970
+ round: epochNumber,
5971
+ repository: z3.string().min(1),
5972
+ pr: z3.number().int().positive(),
5973
+ branch: z3.string().min(1),
5974
+ baseSha: z3.string().min(1).nullable(),
5975
+ headSha: z3.string().min(1),
5976
+ briefHash: z3.string().min(1).nullable(),
5977
+ objectivesVersion: z3.string().min(1).nullable(),
5978
+ rulingOrdinal: epochNumber,
5979
+ policyDigest: z3.string().min(1),
5980
+ recordedAt: isoTimestamp
5981
+ }).strict();
5982
+ var roundHeadIdentity = z3.object({ round: z3.number().int().positive(), head: z3.string().min(1) }).strict();
5983
+ var LoopBudgetsSchema = z3.object({
5984
+ mechanicalRetries: z3.number().int().nonnegative(),
5985
+ reviewRounds: z3.number().int().nonnegative(),
5986
+ infrastructureRetries: z3.number().int().nonnegative()
5987
+ }).strict();
5988
+ var LoopStateRecordSchema = z3.object({
5989
+ version: z3.literal(1),
5990
+ kind: z3.literal("loop_state"),
5991
+ task: taskId,
5992
+ round: z3.number().int().positive(),
5993
+ phase: z3.string().min(1),
5994
+ pauseReason: z3.string().min(1).nullable(),
5995
+ budgets: LoopBudgetsSchema,
5996
+ heldResult: roundHeadIdentity.nullable(),
5997
+ deliveredFindings: roundHeadIdentity.nullable(),
5998
+ recordedAt: isoTimestamp
5999
+ }).strict();
6000
+ var EffectRecordSchema = z3.object({
6001
+ version: z3.literal(1),
6002
+ kind: z3.literal("effect"),
6003
+ task: taskId,
6004
+ key: z3.string().min(1),
6005
+ operation: z3.string().min(1),
6006
+ target: z3.string().min(1),
6007
+ inputVersion: z3.number().int().nonnegative(),
6008
+ payloadDigest: z3.string().min(1),
6009
+ status: z3.union([z3.literal("started"), z3.literal("verified"), z3.literal("uncertain")]),
6010
+ url: z3.string().optional(),
6011
+ recordedAt: isoTimestamp
6012
+ }).strict();
6013
+ var EscalationRecordSchema = z3.object({
6014
+ version: z3.literal(1),
6015
+ kind: z3.literal("escalation"),
6016
+ task: taskId,
6017
+ escalationId: z3.string().min(1),
6018
+ round: epochNumber,
6019
+ head: z3.string().min(1),
6020
+ branch: z3.string().min(1),
6021
+ pr: z3.number().int().positive().nullable(),
6022
+ runId: z3.string().min(1),
6023
+ pid: z3.number().int().positive(),
6024
+ host: z3.string().min(1),
6025
+ agent: z3.string().min(1).optional(),
6026
+ reason: z3.string().min(1),
6027
+ detail: z3.string().optional(),
6028
+ evidence: z3.string().optional(),
6029
+ attemptedRecovery: z3.string().min(1),
6030
+ requestedDecision: z3.string().min(1),
6031
+ recipient: z3.string().min(1),
6032
+ briefHash: z3.string().min(1).nullable(),
6033
+ objectivesVersion: z3.string().min(1).nullable(),
6034
+ rulingOrdinal: epochNumber,
6035
+ policyDigest: z3.string().min(1),
6036
+ recordedAt: isoTimestamp
6037
+ }).strict();
6038
+ var ResolutionRecordSchema = z3.object({
6039
+ version: z3.literal(1),
6040
+ kind: z3.literal("resolution"),
6041
+ task: taskId,
6042
+ escalationId: z3.string().min(1),
6043
+ decision: z3.union([z3.literal("resume"), z3.literal("cancel")]),
6044
+ authenticatedBy: z3.string().min(1),
6045
+ authenticatedFrom: z3.string().min(1),
6046
+ consumedAt: isoTimestamp
6047
+ }).strict();
5066
6048
  // src/commands/doctrine.ts
5067
6049
  import { execFileSync as execFileSync2 } from "node:child_process";
5068
6050
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
@@ -5121,27 +6103,29 @@ import { execFileSync as execFileSync3 } from "node:child_process";
5121
6103
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, realpathSync, writeFileSync } from "node:fs";
5122
6104
  import { homedir } from "node:os";
5123
6105
  import { dirname as dirname3, join as join3, resolve } from "node:path";
5124
- import { z as z2 } from "zod";
6106
+ import { z as z4 } from "zod";
5125
6107
 
5126
6108
  // src/lib/agent-vendors.ts
5127
6109
  var AGENT_VENDORS = ["skills", "claude", "gemini"];
5128
6110
 
5129
6111
  // src/lib/config.ts
5130
- var EnvEntrySchema = z2.union([
5131
- z2.literal(true),
5132
- z2.object({ optional: z2.literal(true) }),
5133
- z2.object({ anyOf: z2.array(z2.string()).min(2) }),
5134
- z2.string()
6112
+ var EnvEntrySchema = z4.union([
6113
+ z4.literal(true),
6114
+ z4.object({ optional: z4.literal(true) }),
6115
+ z4.object({ anyOf: z4.array(z4.string()).min(2) }),
6116
+ z4.string()
5135
6117
  ]);
5136
- var CheckEntrySchema = z2.object({
5137
- run: z2.string(),
5138
- scope: z2.enum(["diff", "full"]),
5139
- include: z2.array(z2.string()).optional(),
5140
- args: z2.array(z2.string()).optional(),
5141
- timeoutMs: z2.number().optional(),
5142
- env: z2.record(z2.string(), EnvEntrySchema).optional(),
5143
- requiresOpenPr: z2.boolean().optional(),
5144
- ownWorkflow: z2.boolean().optional()
6118
+ var CheckEntrySchema = z4.object({
6119
+ run: z4.string(),
6120
+ scope: z4.enum(["diff", "full"]),
6121
+ include: z4.array(z4.string()).optional(),
6122
+ args: z4.array(z4.string()).optional(),
6123
+ timeoutMs: z4.number().optional(),
6124
+ env: z4.record(z4.string(), EnvEntrySchema).optional(),
6125
+ requiresOpenPr: z4.boolean().optional(),
6126
+ ownWorkflow: z4.boolean().optional(),
6127
+ principalOwed: z4.literal(true).optional(),
6128
+ validates: z4.enum(["body", "issue"]).optional()
5145
6129
  }).superRefine((entry2, ctx) => {
5146
6130
  if (!entry2.env)
5147
6131
  return;
@@ -5151,25 +6135,26 @@ var CheckEntrySchema = z2.object({
5151
6135
  const members = value.anyOf;
5152
6136
  if (new Set(members).size !== members.length) {
5153
6137
  ctx.addIssue({
5154
- code: z2.ZodIssueCode.custom,
6138
+ code: z4.ZodIssueCode.custom,
5155
6139
  message: `env.${key}.anyOf has duplicate members`,
5156
6140
  path: ["env", key, "anyOf"]
5157
6141
  });
5158
6142
  }
5159
6143
  if (!members.includes(key)) {
5160
6144
  ctx.addIssue({
5161
- code: z2.ZodIssueCode.custom,
6145
+ code: z4.ZodIssueCode.custom,
5162
6146
  message: `env.${key}.anyOf must include "${key}" itself as a member`,
5163
6147
  path: ["env", key, "anyOf"]
5164
6148
  });
5165
6149
  }
5166
6150
  }
5167
6151
  });
5168
- var RoleEntrySchema = z2.object({
5169
- contract: z2.string().refine((p) => p.includes("/"), {
6152
+ var RoleEntrySchema = z4.object({
6153
+ contract: z4.string().refine((p) => p.includes("/"), {
5170
6154
  message: 'must be a path (contain at least one "/"), not a bare filename'
5171
6155
  })
5172
6156
  });
6157
+ var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
5173
6158
  var BRIEF_BUILTINS = [
5174
6159
  "tier",
5175
6160
  "testPlan",
@@ -5189,17 +6174,17 @@ var BRIEF_BUILTINS = [
5189
6174
  "briefSections",
5190
6175
  "milestoneShape"
5191
6176
  ];
5192
- var BriefSectionSchema = z2.union([
5193
- z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
5194
- z2.object({ heading: z2.string().min(1), name: z2.string().optional() }),
5195
- z2.object({ field: z2.string().min(1), name: z2.string().optional() }),
5196
- z2.object({ phrase: z2.string().min(1), name: z2.string().optional() })
6177
+ var BriefSectionSchema = z4.union([
6178
+ z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
6179
+ z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
6180
+ z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
6181
+ z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
5197
6182
  ]);
5198
- var BriefSchemaSchema = z2.object({
5199
- pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
5200
- issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
5201
- milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
5202
- ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
6183
+ var BriefSchemaSchema = z4.object({
6184
+ pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
6185
+ issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
6186
+ milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
6187
+ ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
5203
6188
  });
5204
6189
  function isSafeRepoRelPath(p) {
5205
6190
  if (p.length === 0)
@@ -5208,7 +6193,7 @@ function isSafeRepoRelPath(p) {
5208
6193
  return false;
5209
6194
  return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
5210
6195
  }
5211
- var SafeRepoRelPath = z2.string().refine(isSafeRepoRelPath, {
6196
+ var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
5212
6197
  message: "must be a repo-root-relative path with no `..` segment or absolute root"
5213
6198
  });
5214
6199
  var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
@@ -5218,22 +6203,22 @@ function isCanonicalHookBlockPath(p) {
5218
6203
  var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
5219
6204
  message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
5220
6205
  });
5221
- var ManagedBlockRecordSchema = z2.object({
6206
+ var ManagedBlockRecordSchema = z4.object({
5222
6207
  path: ManagedHookBlockPath,
5223
- marker: z2.string(),
5224
- comment: z2.enum(["hash", "html"])
6208
+ marker: z4.string(),
6209
+ comment: z4.enum(["hash", "html"])
5225
6210
  });
5226
- var ManagedManifestSchema = z2.object({
5227
- version: z2.number().int().positive(),
5228
- files: z2.array(SafeRepoRelPath),
5229
- blocks: z2.array(ManagedBlockRecordSchema),
5230
- labels: z2.array(z2.string()),
5231
- agents: z2.array(z2.enum(AGENT_VENDORS)).optional()
6211
+ var ManagedManifestSchema = z4.object({
6212
+ version: z4.number().int().positive(),
6213
+ files: z4.array(SafeRepoRelPath),
6214
+ blocks: z4.array(ManagedBlockRecordSchema),
6215
+ labels: z4.array(z4.string()),
6216
+ agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
5232
6217
  });
5233
- var ProjectEntrySchema = z2.object({
5234
- name: z2.string().min(1),
5235
- description: z2.string().min(1).optional(),
5236
- path: z2.string().min(1).optional()
6218
+ var ProjectEntrySchema = z4.object({
6219
+ name: z4.string().min(1),
6220
+ description: z4.string().min(1).optional(),
6221
+ path: z4.string().min(1).optional()
5237
6222
  });
5238
6223
  function parseTokensCollectDeclaration(value) {
5239
6224
  const m = value.trim().match(/^(\S+)\s+(\S+)$/);
@@ -5244,34 +6229,62 @@ function parseTokensCollectDeclaration(value) {
5244
6229
  return null;
5245
6230
  return { interpreter, script };
5246
6231
  }
5247
- var VinayaConfigSchema = z2.object({
5248
- rings: z2.object({
5249
- ring1_forgeWriteInterception: z2.boolean(),
5250
- ring2_asyncAudits: z2.boolean()
6232
+ var VinayaConfigSchema = z4.object({
6233
+ rings: z4.object({
6234
+ ring1_forgeWriteInterception: z4.boolean(),
6235
+ ring2_asyncAudits: z4.boolean()
5251
6236
  }).optional(),
5252
- checks: z2.record(z2.string(), CheckEntrySchema).optional(),
5253
- roles: z2.record(z2.string(), RoleEntrySchema).optional(),
6237
+ checks: z4.record(z4.string(), CheckEntrySchema).optional(),
6238
+ roles: z4.record(z4.string(), RoleEntrySchema).optional(),
5254
6239
  briefSchema: BriefSchemaSchema.optional(),
5255
6240
  managed: ManagedManifestSchema.optional(),
5256
- principals: z2.array(z2.string()).min(1).optional(),
5257
- releaseActor: z2.string().min(1).optional(),
5258
- ci: z2.object({ setup: z2.string().min(1) }).optional(),
5259
- tokens: z2.object({
5260
- collect: z2.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
6241
+ principals: z4.array(z4.string()).min(1).optional(),
6242
+ releaseActor: z4.string().min(1).optional(),
6243
+ ci: z4.object({ setup: z4.string().min(1) }).optional(),
6244
+ tokens: z4.object({
6245
+ collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
5261
6246
  message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
5262
6247
  })
5263
6248
  }).optional(),
5264
- blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional(),
5265
- proseGates: z2.object({
5266
- doctrineRoot: z2.string().min(1).optional(),
5267
- readerFacingPrefix: z2.string().min(1).optional(),
5268
- readerFacingSuffix: z2.string().min(1).optional(),
5269
- legacySlugDir: z2.string().min(1).optional()
6249
+ blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
6250
+ proseGates: z4.object({
6251
+ doctrineRoot: z4.string().min(1).optional(),
6252
+ readerFacingPrefix: z4.string().min(1).optional(),
6253
+ readerFacingSuffix: z4.string().min(1).optional(),
6254
+ legacySlugDir: z4.string().min(1).optional()
6255
+ }).optional(),
6256
+ projects: z4.array(ProjectEntrySchema).optional(),
6257
+ dispatch: z4.object({
6258
+ timeoutMs: z4.number().int().positive().optional(),
6259
+ killGraceMs: z4.number().int().positive().optional(),
6260
+ agent: z4.enum(["claude", "codex", "gemini"]).optional()
6261
+ }).optional(),
6262
+ reviewPolicy: z4.object({
6263
+ codeReviewThreshold: z4.string().min(1).optional(),
6264
+ securityThreshold: z4.string().min(1).optional(),
6265
+ maxRounds: z4.number().optional()
6266
+ }).optional(),
6267
+ prePush: z4.object({
6268
+ alwaysRun: z4.array(z4.string()).optional()
5270
6269
  }).optional(),
5271
- projects: z2.array(ProjectEntrySchema).optional(),
5272
- dispatch: z2.object({
5273
- timeoutMs: z2.number().int().positive().optional(),
5274
- agent: z2.enum(["claude", "codex", "gemini"]).optional()
6270
+ report: z4.object({
6271
+ commandTimeoutMs: z4.number().int().positive().optional()
6272
+ }).superRefine((report, ctx) => {
6273
+ const commandTimeoutMs = report.commandTimeoutMs;
6274
+ if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
6275
+ ctx.addIssue({
6276
+ code: z4.ZodIssueCode.custom,
6277
+ path: ["commandTimeoutMs"],
6278
+ message: "must be at most 3600000 (1 hour)"
6279
+ });
6280
+ }
6281
+ }).optional(),
6282
+ logPublish: z4.object({
6283
+ issue: z4.number().int().positive().optional(),
6284
+ pr: z4.number().int().positive().optional(),
6285
+ maxChunksPerFlush: z4.number().int().positive().optional()
6286
+ }).refine((v) => !(v.issue !== undefined && v.pr !== undefined), {
6287
+ message: "logPublish: set at most one of issue/pr, not both"
5275
6288
  }).optional()
5276
6289
  });
5277
6290
  var GLOBAL_VINAYA_HOME = join3(homedir(), ".vinaya");
@@ -5346,6 +6359,28 @@ function resolvePrincipalAllowlist(config) {
5346
6359
  function resolveReleaseActor(config) {
5347
6360
  return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
5348
6361
  }
6362
+ function resolveReviewPolicy(config) {
6363
+ const raw = config?.reviewPolicy;
6364
+ if (!raw)
6365
+ return DEFAULT_REVIEW_POLICY;
6366
+ const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
6367
+ if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
6368
+ throw new Error(`vinaya.config.json: reviewPolicy.codeReviewThreshold "${codeReviewThreshold}" is not one of ${CODE_REVIEW_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
6369
+ }
6370
+ const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
6371
+ if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
6372
+ throw new Error(`vinaya.config.json: reviewPolicy.securityThreshold "${securityThreshold}" is not one of ${SECURITY_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
6373
+ }
6374
+ const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
6375
+ if (!Number.isInteger(maxRounds) || maxRounds < 1) {
6376
+ throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
6377
+ }
6378
+ return {
6379
+ codeReviewThreshold,
6380
+ securityThreshold,
6381
+ maxRounds
6382
+ };
6383
+ }
5349
6384
  function trustAnchorRepo() {
5350
6385
  const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
5351
6386
  const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
@@ -5433,8 +6468,11 @@ function emitCheckError(error) {
5433
6468
  }
5434
6469
 
5435
6470
  // src/lib/diff-evidence.ts
6471
+ import { execFileSync as execFileSync7 } from "node:child_process";
6472
+ import { join as join6, relative, resolve as resolve2 } from "node:path";
6473
+
6474
+ // src/lib/brief-assembly.ts
5436
6475
  import { execFileSync as execFileSync5 } from "node:child_process";
5437
- import { join as join4, relative, resolve as resolve2 } from "node:path";
5438
6476
 
5439
6477
  // ../../packages/sources/src/commands.ts
5440
6478
  var COMMANDS = [
@@ -5594,6 +6632,27 @@ var COMMANDS = [
5594
6632
  ],
5595
6633
  status: "shipped"
5596
6634
  },
6635
+ {
6636
+ name: "task status",
6637
+ description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
6638
+ flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
6639
+ details: [
6640
+ "Read-only: one `gh issue list` for every open task Issue across every tranche (title/label resolved through the same `resolveTaskIssueRef` `list-tasks.ts` already uses), the open pull request per branch, and the driver pid record / pause record / publish effect markers under `<outboxRoot>/dev-review-loop/<task>/` — never a `ps` scan, never a re-parse of posted verdict comments to decide `published`.",
6641
+ "`running` names the driver's pid (`review-validity-v1` task 7's pid record); `paused` names the reason from the pause record; `published` means the newest round's reviewer and security verdict effect markers both read `posted`; `no driver` is the fallback when none of the above holds.",
6642
+ "`vinaya task status <tranche> <n>` narrows to one task and adds the last round's held or published verdict lines (`round-<n>-reviewer.md`/`round-<n>-security.md`, whichever their outbox carries) plus the exact `vinaya dev-review-loop --resume <pr>` command when paused."
6643
+ ],
6644
+ status: "shipped"
6645
+ },
6646
+ {
6647
+ name: "task-tools serve",
6648
+ description: "Run the task-operator MCP tool server over stdio — the command both runtime adapters register",
6649
+ details: [
6650
+ "Exposes the task-operator tool catalog (`packages/aeg-core/src/task-tools.ts`) to an agent runtime as one MCP server speaking newline-delimited JSON-RPC 2.0 (`initialize`/`tools/list`/`tools/call`). Claude registers it through the generated `.mcp.json`; Codex through its documented `~/.codex/config.toml` `[mcp_servers]` table — both point at this same command.",
6651
+ "stdout carries JSON-RPC and nothing else: any stray write from the CLI (a forge-read warning) is redirected to stderr, so the protocol stream is never corrupted.",
6652
+ "`task_status`/`task_escalation_read` read; `task_start` starts a run in attended mode only, requiring an authenticated caller from the invocation context (`VINAYA_MCP_CALLER`) and refusing without one — MCP is a transport, not authorization. `task_resume`/`task_cancel` require the same authenticated caller, plus a current Principal ruling read fresh from the run's own PR — never a caller-supplied approval; `task_resume` triggers the existing `dev-review-loop --resume` continuation exactly once per escalation, and `task_cancel` reports a truthful `confirmed`/`pending`/`uncertain` outcome, idempotently, across repeated calls. No unattended start until the worker-isolation boundary and a capability flag land."
6653
+ ],
6654
+ status: "shipped"
6655
+ },
5597
6656
  {
5598
6657
  name: "pr create",
5599
6658
  description: "Open a pull request after full brief-schema validation",
@@ -5738,6 +6797,30 @@ var COMMANDS = [
5738
6797
  ],
5739
6798
  status: "shipped"
5740
6799
  },
6800
+ {
6801
+ name: "log export-artifact",
6802
+ description: "Copy this run's own local outbox into a file, for the generated task-path workflow's own upload step",
6803
+ flags: [],
6804
+ details: [
6805
+ "Concatenates every `*.ndjson` file under `~/.vinaya/outbox/` into `<dest>`, verbatim — no filtering, no second redaction pass (lines are already redacted at `log()`'s own write boundary). Writes nothing when the outbox is empty, and exits 0 either way: no gate events this run is a legitimate outcome, never a failure.",
6806
+ "Runs inside the generated `vinaya-checks.yml` job, which holds no forge-write credential — this command never calls the forge. Both this step and the `actions/upload-artifact` step after it run `if: always()`, so a cancelled or failed job still exports whatever partial outbox it has."
6807
+ ],
6808
+ status: "shipped"
6809
+ },
6810
+ {
6811
+ name: "log collect-artifact",
6812
+ description: "Validate a downloaded task-log artifact and publish the valid records through the existing flush path",
6813
+ flags: [
6814
+ { flag: "--pr", description: "The pull request this artifact belongs to" },
6815
+ { flag: "--repo", description: "Expected `owner/repo` — a provenance cross-check against each record" }
6816
+ ],
6817
+ details: [
6818
+ "Runs inside the generated `vinaya-task-log-collector.yml` workflow — a `workflow_run`-triggered job on the default branch, holding the write credential the task-path job that produced the artifact never gets. `<path>` is already downloaded by that workflow, scoped to a specific, API-verified run id — a provenance guarantee this command never re-derives from the artifact bytes.",
6819
+ "Never executes, imports, or evaluates the artifact's content: every line is read as text and validated (schema, an 8 MiB whole-artifact size cap, redaction, and a repo cross-check) before anything is trusted. A rejected line becomes a named gap — printed, never a silent drop — and the artifact is never partially trusted past that check into executing anything.",
6820
+ "The valid, re-redacted survivors are appended into the same local outbox `vinaya log flush` would read for that PR's Issue, then published through the existing `flushOutbox` unmodified — reusing its chunking, its `<!-- aeg:log:… -->` marker, and its idempotent-retry read, rather than a second publisher. A retried collector run (a fresh runner, the same forge state) therefore dedupes for free: the marker already on the forge is acknowledged, never re-posted."
6821
+ ],
6822
+ status: "shipped"
6823
+ },
5741
6824
  {
5742
6825
  name: "milestone create",
5743
6826
  description: "Create a GitHub Milestone from a validated body",
@@ -6103,10 +7186,10 @@ function createForgeSource(config) {
6103
7186
  };
6104
7187
  }
6105
7188
  // ../../packages/sources/src/select-source.ts
6106
- import { z as z3 } from "zod";
6107
- var StateSourceConfigSchema = z3.discriminatedUnion("kind", [
6108
- z3.object({ kind: z3.literal("forge"), owner: z3.string(), repo: z3.string() }),
6109
- z3.object({ kind: z3.literal("file"), root: z3.string().optional() })
7189
+ import { z as z5 } from "zod";
7190
+ var StateSourceConfigSchema = z5.discriminatedUnion("kind", [
7191
+ z5.object({ kind: z5.literal("forge"), owner: z5.string(), repo: z5.string() }),
7192
+ z5.object({ kind: z5.literal("file"), root: z5.string().optional() })
6110
7193
  ]);
6111
7194
  // src/checks/edge-resolve.ts
6112
7195
  import { execFileSync as execFileSync4 } from "node:child_process";
@@ -6226,6 +7309,816 @@ async function resolveEdge(id, taskById, factsByTaskId, repo, resolveSibling = d
6226
7309
  };
6227
7310
  }
6228
7311
 
7312
+ // src/lib/brief-assembly.ts
7313
+ function git(args) {
7314
+ try {
7315
+ return execFileSync5("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
7316
+ } catch {
7317
+ return "";
7318
+ }
7319
+ }
7320
+ function expandGlob(glob) {
7321
+ const out = git(["ls-files", "--", glob]);
7322
+ return out.split(`
7323
+ `).map((s) => s.trim()).filter(Boolean);
7324
+ }
7325
+ var DRAFT_ISSUE_SENTINEL = Number.MAX_SAFE_INTEGER;
7326
+
7327
+ // src/checks/registry.ts
7328
+ import { existsSync as existsSync4 } from "node:fs";
7329
+ import { join as join4 } from "node:path";
7330
+ var DIST_BIN_DIR = join4(packageRoot(import.meta.url), "dist", "checks", "bin");
7331
+ var SRC_BIN_DIR = join4(packageRoot(import.meta.url), "src", "checks", "bin");
7332
+ var BIN_DIR = existsSync4(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
7333
+ var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
7334
+ function bin(name) {
7335
+ return join4(BIN_DIR, `${name}${BIN_EXT}`);
7336
+ }
7337
+ var REGISTRY = [
7338
+ [
7339
+ {
7340
+ name: "brief-shape",
7341
+ validates: "body",
7342
+ run: bin("check-brief-shape"),
7343
+ scope: "diff",
7344
+ timeoutMs: 15000,
7345
+ env: {
7346
+ PR_BODY: { optional: true },
7347
+ BRANCH: { optional: true },
7348
+ PR_NUMBER: { optional: true },
7349
+ GITHUB_TOKEN: { optional: true },
7350
+ GH_TOKEN: { optional: true }
7351
+ }
7352
+ },
7353
+ 0
7354
+ ],
7355
+ [
7356
+ {
7357
+ name: "pr-report-density",
7358
+ validates: "body",
7359
+ run: bin("check-pr-report-density"),
7360
+ scope: "diff",
7361
+ timeoutMs: 15000,
7362
+ env: {
7363
+ PR_BODY: { optional: true }
7364
+ }
7365
+ },
7366
+ 0
7367
+ ],
7368
+ [
7369
+ {
7370
+ name: "doc-coverage",
7371
+ validates: "body",
7372
+ run: bin("check-doc-coverage"),
7373
+ scope: "diff",
7374
+ timeoutMs: 15000,
7375
+ env: {
7376
+ BASE_SHA: { optional: true },
7377
+ PR_BODY: { optional: true },
7378
+ PR_BODY_FILE: { optional: true },
7379
+ PR_NUMBER: { optional: true },
7380
+ GITHUB_REPOSITORY: { optional: true },
7381
+ GITHUB_TOKEN: { optional: true },
7382
+ GH_TOKEN: { optional: true }
7383
+ }
7384
+ },
7385
+ 0
7386
+ ],
7387
+ [
7388
+ {
7389
+ name: "coherence",
7390
+ run: bin("check-coherence"),
7391
+ scope: "full",
7392
+ timeoutMs: 30000,
7393
+ env: {
7394
+ AEG_REPO: { optional: true },
7395
+ BRANCH: { optional: true },
7396
+ GITHUB_TOKEN: { optional: true },
7397
+ GH_TOKEN: { optional: true }
7398
+ }
7399
+ },
7400
+ 0
7401
+ ],
7402
+ [
7403
+ {
7404
+ name: "pr-premise-reassert",
7405
+ validates: "body",
7406
+ run: bin("check-pr-premise-reassert"),
7407
+ scope: "diff",
7408
+ timeoutMs: 15000,
7409
+ env: {
7410
+ PR_BODY: { optional: true }
7411
+ }
7412
+ },
7413
+ 0
7414
+ ],
7415
+ [
7416
+ {
7417
+ name: "dispatch-readiness",
7418
+ run: bin("check-dispatch-readiness"),
7419
+ scope: "full",
7420
+ timeoutMs: 30000,
7421
+ env: {
7422
+ AEG_REPO: { optional: true },
7423
+ BRANCH: { optional: true },
7424
+ GITHUB_TOKEN: { optional: true },
7425
+ GH_TOKEN: { optional: true },
7426
+ PREMISE_FILE: { optional: true }
7427
+ }
7428
+ },
7429
+ 0
7430
+ ],
7431
+ [
7432
+ {
7433
+ name: "closes-n",
7434
+ validates: "body",
7435
+ run: bin("check-closes-n"),
7436
+ scope: "diff",
7437
+ timeoutMs: 15000,
7438
+ requiresOpenPr: true,
7439
+ env: {
7440
+ BRANCH: { optional: true },
7441
+ PR_BODY: { optional: true },
7442
+ AEG_REPO: { optional: true },
7443
+ GITHUB_TOKEN: { optional: true },
7444
+ GH_TOKEN: { optional: true }
7445
+ }
7446
+ },
7447
+ 1
7448
+ ],
7449
+ [
7450
+ {
7451
+ name: "single-plan-pr",
7452
+ run: bin("check-single-plan-pr"),
7453
+ scope: "diff",
7454
+ timeoutMs: 15000,
7455
+ env: {
7456
+ BASE_SHA: { optional: true },
7457
+ PR_NUMBER: { optional: true },
7458
+ GITHUB_TOKEN: { optional: true },
7459
+ GH_TOKEN: { optional: true }
7460
+ }
7461
+ },
7462
+ 0
7463
+ ],
7464
+ [
7465
+ {
7466
+ name: "surface-scope",
7467
+ run: bin("check-surface-scope"),
7468
+ scope: "diff",
7469
+ timeoutMs: 15000,
7470
+ env: {
7471
+ AEG_REPO: { optional: true },
7472
+ BASE_SHA: { optional: true },
7473
+ BRANCH: { optional: true },
7474
+ GITHUB_TOKEN: { optional: true },
7475
+ GH_TOKEN: { optional: true }
7476
+ }
7477
+ },
7478
+ 0
7479
+ ],
7480
+ [
7481
+ {
7482
+ name: "test-plan",
7483
+ validates: "body",
7484
+ run: bin("check-test-plan"),
7485
+ scope: "diff",
7486
+ timeoutMs: 15000,
7487
+ requiresOpenPr: true,
7488
+ principalOwed: true,
7489
+ env: {
7490
+ PR_BODY: { optional: true },
7491
+ BRANCH: { optional: true }
7492
+ }
7493
+ },
7494
+ 1
7495
+ ],
7496
+ [
7497
+ {
7498
+ name: "body-bare-digits",
7499
+ validates: "body",
7500
+ run: bin("check-body-bare-digits"),
7501
+ scope: "diff",
7502
+ timeoutMs: 15000,
7503
+ requiresOpenPr: true,
7504
+ ownWorkflow: true,
7505
+ env: {
7506
+ PR_BODY: { optional: true },
7507
+ PR_NUMBER: { optional: true },
7508
+ GITHUB_REPOSITORY: { optional: true },
7509
+ GITHUB_TOKEN: { optional: true },
7510
+ GH_TOKEN: { optional: true }
7511
+ }
7512
+ },
7513
+ 1
7514
+ ],
7515
+ [
7516
+ {
7517
+ name: "token-report",
7518
+ validates: "body",
7519
+ run: bin("check-token-report"),
7520
+ scope: "diff",
7521
+ timeoutMs: 15000,
7522
+ requiresOpenPr: true,
7523
+ env: {
7524
+ PR_BODY: { optional: true },
7525
+ BRANCH: { optional: true },
7526
+ CLAUDE_PROJECT_DIR: { optional: true },
7527
+ CLAUDE_CODE_SESSION_ID: { optional: true }
7528
+ }
7529
+ },
7530
+ 1
7531
+ ],
7532
+ [
7533
+ {
7534
+ name: "no-disk-state",
7535
+ run: bin("check-no-disk-state"),
7536
+ scope: "diff",
7537
+ timeoutMs: 15000,
7538
+ env: { BASE_SHA: { optional: true } }
7539
+ },
7540
+ 0
7541
+ ],
7542
+ [
7543
+ {
7544
+ name: "registry-gates",
7545
+ run: bin("check-registry-gates"),
7546
+ scope: "full",
7547
+ timeoutMs: 30000,
7548
+ env: {
7549
+ AEG_REPO: { optional: true },
7550
+ GITHUB_TOKEN: { optional: true },
7551
+ GH_TOKEN: { optional: true }
7552
+ }
7553
+ },
7554
+ 0
7555
+ ],
7556
+ [
7557
+ {
7558
+ name: "review-gate",
7559
+ run: bin("check-review-gate"),
7560
+ scope: "full",
7561
+ timeoutMs: 30000,
7562
+ ownWorkflow: true,
7563
+ env: {
7564
+ PR_NUMBER: { optional: true },
7565
+ GITHUB_REPOSITORY: { optional: true },
7566
+ GITHUB_TOKEN: { optional: true },
7567
+ GH_TOKEN: { optional: true }
7568
+ }
7569
+ },
7570
+ 1
7571
+ ],
7572
+ [
7573
+ {
7574
+ name: "branch-topology",
7575
+ run: bin("check-branch-topology"),
7576
+ scope: "full",
7577
+ timeoutMs: 30000,
7578
+ env: {
7579
+ BRANCH: { optional: true },
7580
+ AEG_REPO: { optional: true },
7581
+ GITHUB_TOKEN: { optional: true },
7582
+ GH_TOKEN: { optional: true }
7583
+ }
7584
+ },
7585
+ 0
7586
+ ],
7587
+ [
7588
+ {
7589
+ name: "dead-branch-push",
7590
+ run: bin("check-dead-branch-push"),
7591
+ scope: "full",
7592
+ timeoutMs: 30000,
7593
+ env: {
7594
+ BRANCH: { optional: true },
7595
+ GITHUB_TOKEN: { optional: true },
7596
+ GH_TOKEN: { optional: true }
7597
+ }
7598
+ },
7599
+ 0
7600
+ ],
7601
+ [
7602
+ {
7603
+ name: "first-push-dispatch",
7604
+ run: bin("check-first-push-dispatch"),
7605
+ scope: "full",
7606
+ timeoutMs: 30000,
7607
+ env: {
7608
+ BRANCH: { optional: true },
7609
+ AEG_REPO: { optional: true },
7610
+ GITHUB_TOKEN: { optional: true },
7611
+ GH_TOKEN: { optional: true }
7612
+ }
7613
+ },
7614
+ 0
7615
+ ],
7616
+ [
7617
+ {
7618
+ name: "doc-coverage-push",
7619
+ validates: "body",
7620
+ run: bin("check-doc-coverage-push"),
7621
+ scope: "diff",
7622
+ timeoutMs: 15000,
7623
+ env: {
7624
+ OVERRIDE_DOCS: { optional: true },
7625
+ BASE_SHA: { optional: true },
7626
+ PR_BODY: { optional: true },
7627
+ PR_BODY_FILE: { optional: true },
7628
+ PR_LABELS: { optional: true },
7629
+ WAIVER_LABEL_ACTOR: { optional: true },
7630
+ GITHUB_REPOSITORY: { optional: true },
7631
+ GITHUB_TOKEN: { optional: true },
7632
+ GH_TOKEN: { optional: true }
7633
+ }
7634
+ },
7635
+ 0
7636
+ ],
7637
+ [
7638
+ {
7639
+ name: "issue-assignment",
7640
+ run: bin("check-issue-assignment"),
7641
+ scope: "full",
7642
+ timeoutMs: 30000,
7643
+ env: {
7644
+ AEG_REPO: { optional: true },
7645
+ BRANCH: { optional: true },
7646
+ GITHUB_TOKEN: { optional: true },
7647
+ GH_TOKEN: { optional: true }
7648
+ }
7649
+ },
7650
+ 0
7651
+ ],
7652
+ [
7653
+ {
7654
+ name: "evidence-fresh",
7655
+ validates: "body",
7656
+ run: bin("check-evidence-fresh"),
7657
+ scope: "diff",
7658
+ timeoutMs: 15000,
7659
+ requiresOpenPr: true,
7660
+ env: {
7661
+ PR_NUMBER: { optional: true },
7662
+ PR_BODY: { optional: true },
7663
+ BASE_SHA: { optional: true },
7664
+ GITHUB_TOKEN: { optional: true },
7665
+ GH_TOKEN: { optional: true }
7666
+ }
7667
+ },
7668
+ 1
7669
+ ],
7670
+ [
7671
+ {
7672
+ name: "reader-resolvable-prose",
7673
+ run: bin("check-reader-resolvable-prose"),
7674
+ scope: "full",
7675
+ timeoutMs: 30000,
7676
+ env: {},
7677
+ include: [
7678
+ "aeg-root/**/*.md",
7679
+ "apps/cli/src/**",
7680
+ ".github/workflows/**",
7681
+ ".vinaya/**",
7682
+ "apps/cli/README.md",
7683
+ "packages/sources/README.md"
7684
+ ]
7685
+ },
7686
+ 0
7687
+ ],
7688
+ [
7689
+ {
7690
+ name: "retired-vocabulary",
7691
+ run: bin("check-retired-vocabulary"),
7692
+ scope: "full",
7693
+ timeoutMs: 30000,
7694
+ env: {},
7695
+ include: ["aeg-root/**/*.md"]
7696
+ },
7697
+ 0
7698
+ ],
7699
+ [
7700
+ {
7701
+ name: "doctrine-portability",
7702
+ run: bin("check-doctrine-portability"),
7703
+ scope: "full",
7704
+ timeoutMs: 30000,
7705
+ env: { BASE_SHA: { optional: true } },
7706
+ include: ["aeg-root/**/*.md"]
7707
+ },
7708
+ 0
7709
+ ],
7710
+ [
7711
+ {
7712
+ name: "doctrine-no-procedures",
7713
+ run: bin("check-doctrine-no-procedures"),
7714
+ scope: "full",
7715
+ timeoutMs: 30000,
7716
+ env: {},
7717
+ include: ["aeg-root/**/*.md"]
7718
+ },
7719
+ 0
7720
+ ],
7721
+ [
7722
+ {
7723
+ name: "exec-bits",
7724
+ run: bin("check-exec-bits"),
7725
+ scope: "diff",
7726
+ timeoutMs: 30000,
7727
+ env: {}
7728
+ },
7729
+ 0
7730
+ ],
7731
+ [
7732
+ {
7733
+ name: "workspace-escape",
7734
+ run: bin("check-workspace-escape"),
7735
+ scope: "full",
7736
+ timeoutMs: 30000,
7737
+ env: {},
7738
+ include: ["aeg-root/**/*.md"]
7739
+ },
7740
+ 0
7741
+ ],
7742
+ [
7743
+ {
7744
+ name: "changeset-coverage",
7745
+ run: bin("check-changeset-coverage"),
7746
+ scope: "diff",
7747
+ timeoutMs: 15000,
7748
+ env: {}
7749
+ },
7750
+ 0
7751
+ ],
7752
+ [
7753
+ {
7754
+ name: "quoted-command",
7755
+ run: bin("check-quoted-command"),
7756
+ scope: "diff",
7757
+ timeoutMs: 15000,
7758
+ env: {}
7759
+ },
7760
+ 0
7761
+ ],
7762
+ [
7763
+ {
7764
+ name: "main-branch-refusal",
7765
+ run: bin("check-main-branch-refusal"),
7766
+ scope: "full",
7767
+ timeoutMs: 15000,
7768
+ env: { VINAYA_PUSH_REFS: { optional: true } }
7769
+ },
7770
+ 0
7771
+ ],
7772
+ [
7773
+ {
7774
+ name: "token-collection-wired",
7775
+ run: bin("check-token-collection-wired"),
7776
+ scope: "full",
7777
+ timeoutMs: 15000,
7778
+ env: {
7779
+ CLAUDE_PROJECT_DIR: { optional: true },
7780
+ CLAUDE_CODE_SESSION_ID: { optional: true }
7781
+ }
7782
+ },
7783
+ 0
7784
+ ],
7785
+ [
7786
+ {
7787
+ name: "issue-title-grammar",
7788
+ run: bin("check-issue-title-grammar"),
7789
+ validates: "issue",
7790
+ scope: "full",
7791
+ ownWorkflow: true,
7792
+ timeoutMs: 15000,
7793
+ env: { ISSUE_TITLE: { optional: true } }
7794
+ },
7795
+ 1
7796
+ ],
7797
+ [
7798
+ {
7799
+ name: "issue-objectives-numbering",
7800
+ run: bin("check-issue-objectives-numbering"),
7801
+ validates: "issue",
7802
+ scope: "full",
7803
+ ownWorkflow: true,
7804
+ timeoutMs: 15000,
7805
+ env: { ISSUE_BODY: { optional: true }, ISSUE_NUMBER: { optional: true } }
7806
+ },
7807
+ 1
7808
+ ],
7809
+ [
7810
+ {
7811
+ name: "issue-parts-coverage",
7812
+ run: bin("check-issue-parts-coverage"),
7813
+ validates: "issue",
7814
+ scope: "full",
7815
+ ownWorkflow: true,
7816
+ timeoutMs: 15000,
7817
+ env: { ISSUE_BODY: { optional: true } }
7818
+ },
7819
+ 1
7820
+ ],
7821
+ [
7822
+ {
7823
+ name: "issue-surface-globs",
7824
+ run: bin("check-issue-surface-globs"),
7825
+ validates: "issue",
7826
+ scope: "full",
7827
+ ownWorkflow: true,
7828
+ timeoutMs: 15000,
7829
+ env: { ISSUE_BODY: { optional: true } }
7830
+ },
7831
+ 1
7832
+ ],
7833
+ [
7834
+ {
7835
+ name: "issue-tranche-label",
7836
+ run: bin("check-issue-tranche-label"),
7837
+ validates: "issue",
7838
+ scope: "full",
7839
+ ownWorkflow: true,
7840
+ timeoutMs: 15000,
7841
+ env: { ISSUE_BODY: { optional: true }, ISSUE_LABELS: { optional: true } }
7842
+ },
7843
+ 1
7844
+ ],
7845
+ [
7846
+ {
7847
+ name: "issue-milestone-attach",
7848
+ run: bin("check-issue-milestone-attach"),
7849
+ validates: "issue",
7850
+ scope: "full",
7851
+ ownWorkflow: true,
7852
+ timeoutMs: 15000,
7853
+ env: {
7854
+ ISSUE_LABELS: { optional: true },
7855
+ CURRENT_MILESTONE_TITLE: { optional: true },
7856
+ RESOLVED_MILESTONE_TITLE: { optional: true }
7857
+ }
7858
+ },
7859
+ 1
7860
+ ]
7861
+ ];
7862
+ function coreCheckRegistry() {
7863
+ return REGISTRY.map(([spec]) => spec);
7864
+ }
7865
+ var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
7866
+
7867
+ // src/lib/log-sink.ts
7868
+ import { execFileSync as execFileSync6 } from "node:child_process";
7869
+ import { randomUUID } from "node:crypto";
7870
+ import {
7871
+ closeSync as closeSync2,
7872
+ constants as fsConstants2,
7873
+ existsSync as existsSync5,
7874
+ fstatSync as fstatSync2,
7875
+ mkdirSync as mkdirSync2,
7876
+ openSync as openSync2,
7877
+ readFileSync as readFileSync4,
7878
+ renameSync,
7879
+ writeSync
7880
+ } from "node:fs";
7881
+ import { hostname as osHostname, homedir as homedir2 } from "node:os";
7882
+ import { dirname as dirname4, join as join5 } from "node:path";
7883
+ var OUTBOX_MAX_BYTES = 8 * 1024 * 1024;
7884
+ function isEnoent(err) {
7885
+ return typeof err === "object" && err !== null && err.code === "ENOENT";
7886
+ }
7887
+ function resolveDoctrine(cwd, vinayaVersion) {
7888
+ let toplevel;
7889
+ try {
7890
+ toplevel = execFileSync6("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
7891
+ encoding: "utf8",
7892
+ stdio: ["ignore", "pipe", "pipe"]
7893
+ }).trim();
7894
+ } catch (err) {
7895
+ return isEnoent(err) ? "unknown" : vinayaVersion;
7896
+ }
7897
+ if (!existsSync5(join5(toplevel, "aeg-root", "roles")))
7898
+ return vinayaVersion;
7899
+ try {
7900
+ const sha = execFileSync6("git", ["-C", toplevel, "rev-parse", "HEAD:aeg-root"], {
7901
+ encoding: "utf8",
7902
+ stdio: ["ignore", "pipe", "pipe"]
7903
+ }).trim();
7904
+ return `aeg-root@${sha}`;
7905
+ } catch {
7906
+ return vinayaVersion;
7907
+ }
7908
+ }
7909
+ function readVinayaVersion() {
7910
+ try {
7911
+ const pkg = JSON.parse(readFileSync4(join5(packageRoot(import.meta.url), "package.json"), "utf8"));
7912
+ return pkg.version ?? "unknown";
7913
+ } catch {
7914
+ return "unknown";
7915
+ }
7916
+ }
7917
+ var SAFE_PATH_SEGMENT = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
7918
+ function isSafeRepoSegment(segment) {
7919
+ return SAFE_PATH_SEGMENT.test(segment) && !segment.includes("..");
7920
+ }
7921
+ function outboxPathFor(deps, repo, issue) {
7922
+ const dirName = repo ? `${repo.owner}-${repo.repo}` : "unresolved";
7923
+ const fileName = `${issue ?? "none"}.ndjson`;
7924
+ return join5(deps.outboxRoot(), dirName, fileName);
7925
+ }
7926
+ function hostFromEnv(env) {
7927
+ if (env.GITHUB_ACTIONS)
7928
+ return "ci";
7929
+ if (env.VINAYA_HOST === "hook")
7930
+ return "hook";
7931
+ if (env.VINAYA_HOST === "loop")
7932
+ return "loop";
7933
+ return "cli";
7934
+ }
7935
+ function defaultDeps() {
7936
+ return {
7937
+ outboxRoot: () => join5(GLOBAL_VINAYA_HOME, "outbox"),
7938
+ home: () => homedir2(),
7939
+ hostname: () => osHostname(),
7940
+ cwd: () => process.cwd(),
7941
+ now: () => new Date,
7942
+ env: () => process.env,
7943
+ resolveRepo: () => resolveRepo(),
7944
+ vinayaVersion: () => readVinayaVersion(),
7945
+ stderr: (message) => {
7946
+ process.stderr.write(message);
7947
+ },
7948
+ inputVersions: () => {
7949
+ return;
7950
+ }
7951
+ };
7952
+ }
7953
+ var APPEND_OPEN_FLAGS = fsConstants2.O_WRONLY | fsConstants2.O_APPEND | fsConstants2.O_CREAT | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK;
7954
+ function guardedAppendOpen(path) {
7955
+ try {
7956
+ return openSync2(path, APPEND_OPEN_FLAGS, 384);
7957
+ } catch {
7958
+ return;
7959
+ }
7960
+ }
7961
+ function reportRotationOverflow(backupPath, warn) {
7962
+ let raw;
7963
+ try {
7964
+ raw = readFileSync4(backupPath, "utf8");
7965
+ } catch {
7966
+ return;
7967
+ }
7968
+ const lines = raw.split(`
7969
+ `).filter((l) => l.length > 0);
7970
+ if (lines.length === 0)
7971
+ return;
7972
+ const droppedIdentities = lines.map((line, i) => {
7973
+ try {
7974
+ return recordIdentity(JSON.parse(line)) ?? `unreadable:${i}`;
7975
+ } catch {
7976
+ return `unreadable:${i}`;
7977
+ }
7978
+ });
7979
+ const diagnostic = { reason: "capacity", dropped: droppedIdentities.length, droppedIdentities };
7980
+ const shown = diagnostic.droppedIdentities.slice(0, 20);
7981
+ const more = diagnostic.dropped > shown.length ? `, +${diagnostic.dropped - shown.length} more` : "";
7982
+ warn(`vinaya: log outbox rotation is overwriting ${backupPath} — ${diagnostic.dropped} record(s) permanently lost: ${shown.join(", ")}${more}
7983
+ `);
7984
+ }
7985
+ function appendLine(path, line, warn) {
7986
+ try {
7987
+ mkdirSync2(dirname4(path), { recursive: true, mode: 448 });
7988
+ let fd = guardedAppendOpen(path);
7989
+ if (fd === undefined) {
7990
+ warn(`vinaya: log outbox target could not be opened (symlink, FIFO, or unwritable) — refusing: ${path}
7991
+ `);
7992
+ return;
7993
+ }
7994
+ let closed = false;
7995
+ try {
7996
+ const stat = fstatSync2(fd);
7997
+ if (!stat.isFile()) {
7998
+ warn(`vinaya: log outbox target is not a regular file — refusing to write: ${path}
7999
+ `);
8000
+ return;
8001
+ }
8002
+ if (stat.size > OUTBOX_MAX_BYTES) {
8003
+ closeSync2(fd);
8004
+ closed = true;
8005
+ const backupPath = path.replace(/\.ndjson$/, ".1.ndjson");
8006
+ reportRotationOverflow(backupPath, warn);
8007
+ renameSync(path, backupPath);
8008
+ const rotated = guardedAppendOpen(path);
8009
+ if (rotated === undefined) {
8010
+ warn(`vinaya: log outbox target could not be reopened after rotation — refusing: ${path}
8011
+ `);
8012
+ return;
8013
+ }
8014
+ fd = rotated;
8015
+ closed = false;
8016
+ }
8017
+ writeSync(fd, line);
8018
+ } finally {
8019
+ if (!closed)
8020
+ closeSync2(fd);
8021
+ }
8022
+ } catch (err) {
8023
+ warn(`vinaya: log outbox write failed — ${err instanceof Error ? err.message : String(err)}
8024
+ `);
8025
+ }
8026
+ }
8027
+ function createLogSink(overrides = {}) {
8028
+ const deps = { ...defaultDeps(), ...overrides };
8029
+ const runId = deps.env().VINAYA_RUN_ID || randomUUID();
8030
+ const processId = randomUUID();
8031
+ let seq = 0;
8032
+ let warned = false;
8033
+ const warnOnce = (message) => {
8034
+ if (warned)
8035
+ return;
8036
+ warned = true;
8037
+ try {
8038
+ deps.stderr(message);
8039
+ } catch {}
8040
+ };
8041
+ let doctrineCache;
8042
+ const doctrine = () => {
8043
+ if (doctrineCache === undefined)
8044
+ doctrineCache = resolveDoctrine(deps.cwd(), deps.vinayaVersion());
8045
+ return doctrineCache;
8046
+ };
8047
+ let resolveRepoCache;
8048
+ const resolveRepoOnce = () => {
8049
+ if (resolveRepoCache === undefined)
8050
+ resolveRepoCache = deps.resolveRepo();
8051
+ return resolveRepoCache;
8052
+ };
8053
+ function log(e) {
8054
+ try {
8055
+ const env = deps.env();
8056
+ const host = hostFromEnv(env);
8057
+ const now = deps.now();
8058
+ const mySeq = seq++;
8059
+ resolveRepoOnce().then((resolved) => {
8060
+ const repo = resolved && isSafeRepoSegment(resolved.owner) && isSafeRepoSegment(resolved.repo) ? resolved : null;
8061
+ const header = buildHeader({
8062
+ now,
8063
+ runId,
8064
+ seq: mySeq,
8065
+ repo: repo ? `${repo.owner}/${repo.repo}` : null,
8066
+ vinaya: deps.vinayaVersion(),
8067
+ doctrine: doctrine(),
8068
+ host,
8069
+ hostname: deps.hostname(),
8070
+ env: {
8071
+ role: env.VINAYA_ROLE,
8072
+ task: env.VINAYA_TASK,
8073
+ round: env.VINAYA_ROUND,
8074
+ run: env.VINAYA_RUN,
8075
+ attempt: env.VINAYA_ATTEMPT,
8076
+ parent: env.VINAYA_PARENT_EVENT
8077
+ },
8078
+ eventId: randomUUID(),
8079
+ processId,
8080
+ inputVersions: deps.inputVersions()
8081
+ });
8082
+ const full = { ...e, ...header };
8083
+ const parsed = LogEventSchema.safeParse(full);
8084
+ if (!parsed.success) {
8085
+ warnOnce(`vinaya: log() refused an invalid payload — ${parsed.error.issues[0]?.message ?? "schema violation"}
8086
+ `);
8087
+ return;
8088
+ }
8089
+ const line = `${JSON.stringify(redact(parsed.data, deps.home()))}
8090
+ `;
8091
+ appendLine(outboxPathFor(deps, repo, header.subject.issue), line, warnOnce);
8092
+ }).catch((err) => {
8093
+ warnOnce(`vinaya: log() failed — ${err instanceof Error ? err.message : String(err)}
8094
+ `);
8095
+ });
8096
+ } catch (err) {
8097
+ warnOnce(`vinaya: log() failed — ${err instanceof Error ? err.message : String(err)}
8098
+ `);
8099
+ }
8100
+ }
8101
+ const warmup = () => {
8102
+ doctrine();
8103
+ const originalWarn = console.warn;
8104
+ console.warn = () => {};
8105
+ resolveRepoOnce().finally(() => {
8106
+ console.warn = originalWarn;
8107
+ });
8108
+ };
8109
+ return { log, runId, warmup };
8110
+ }
8111
+ var defaultSink = createLogSink();
8112
+
8113
+ // src/checks/runner.ts
8114
+ var activeChecks = new Map;
8115
+
8116
+ // src/lib/forge-write.ts
8117
+ var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
8118
+ "depends-on-not-merged",
8119
+ "conflicts-with"
8120
+ ]);
8121
+
6229
8122
  // src/commands/review-post.ts
6230
8123
  function parseChangedLineRanges(diffOutput) {
6231
8124
  const result = {};
@@ -6257,7 +8150,7 @@ function parseChangedLineRanges(diffOutput) {
6257
8150
  function fileDiffAgainst(ref, path) {
6258
8151
  let out;
6259
8152
  try {
6260
- out = execFileSync5("git", ["diff", `${ref}...HEAD`, "--", path], {
8153
+ out = execFileSync7("git", ["diff", `${ref}...HEAD`, "--", path], {
6261
8154
  encoding: "utf8",
6262
8155
  stdio: ["ignore", "pipe", "pipe"]
6263
8156
  }).trim();
@@ -6268,7 +8161,7 @@ function fileDiffAgainst(ref, path) {
6268
8161
  }
6269
8162
  function revParse(ref) {
6270
8163
  try {
6271
- return execFileSync5("git", ["rev-parse", "--verify", ref], {
8164
+ return execFileSync7("git", ["rev-parse", "--verify", ref], {
6272
8165
  encoding: "utf8",
6273
8166
  stdio: ["ignore", "pipe", "pipe"]
6274
8167
  }).trim();
@@ -6278,7 +8171,7 @@ function revParse(ref) {
6278
8171
  }
6279
8172
  function repoRoot() {
6280
8173
  try {
6281
- return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
8174
+ return execFileSync7("git", ["rev-parse", "--show-toplevel"], {
6282
8175
  encoding: "utf8",
6283
8176
  stdio: ["ignore", "pipe", "pipe"]
6284
8177
  }).trim();
@@ -6289,7 +8182,7 @@ function repoRoot() {
6289
8182
  function changedFiles(base) {
6290
8183
  let out;
6291
8184
  try {
6292
- out = execFileSync5("git", ["diff", "--name-only", `${base}...HEAD`], {
8185
+ out = execFileSync7("git", ["diff", "--name-only", `${base}...HEAD`], {
6293
8186
  encoding: "utf8",
6294
8187
  stdio: ["ignore", "pipe", "pipe"]
6295
8188
  }).trim();
@@ -6314,7 +8207,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
6314
8207
  const relPaths = changedFiles(ref);
6315
8208
  if (relPaths === null)
6316
8209
  continue;
6317
- return { base: ref, root, files: relPaths.map((p) => join4(root, p)) };
8210
+ return { base: ref, root, files: relPaths.map((p) => join6(root, p)) };
6318
8211
  }
6319
8212
  return null;
6320
8213
  }
@@ -6353,7 +8246,7 @@ function resolveCheckDoctrineRoot() {
6353
8246
  return configured;
6354
8247
  const root = repoRoot();
6355
8248
  if (root !== null) {
6356
- const candidate = join5(root, "aeg-root");
8249
+ const candidate = join7(root, "aeg-root");
6357
8250
  if (hasDoctrineEntry(candidate))
6358
8251
  return candidate;
6359
8252
  }
@@ -6371,7 +8264,7 @@ function collect(dir, out = []) {
6371
8264
  return out;
6372
8265
  }
6373
8266
  for (const name of entries) {
6374
- const full = join5(dir, name);
8267
+ const full = join7(dir, name);
6375
8268
  let isDir;
6376
8269
  try {
6377
8270
  isDir = statSync(full).isDirectory();
@@ -6389,7 +8282,7 @@ function collect(dir, out = []) {
6389
8282
  return out;
6390
8283
  }
6391
8284
  function readAll(paths) {
6392
- return paths.map((p) => ({ path: p, content: readFileSync4(p, "utf8") }));
8285
+ return paths.map((p) => ({ path: p, content: readFileSync5(p, "utf8") }));
6393
8286
  }
6394
8287
  function main() {
6395
8288
  if (DOCTRINE_ROOT === null) {