@attalabs/vinaya 0.28.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 (138) hide show
  1. package/README.md +1 -1
  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/roles/developer.md +2 -1
  6. package/aeg-root/roles/operator.md +133 -0
  7. package/aeg-root/roles/planner.md +4 -0
  8. package/aeg-root/skills/aeg-context-packets/SKILL.md +123 -0
  9. package/aeg-root/skills/aeg-context-packets/examples/operator-start.md +30 -0
  10. package/aeg-root/templates/brief-template.md +6 -2
  11. package/dist/checks/bin/check-body-bare-digits.js +376 -16
  12. package/dist/checks/bin/check-branch-topology.js +403 -16
  13. package/dist/checks/bin/check-brief-shape.js +380 -19
  14. package/dist/checks/bin/check-changeset-coverage.js +673 -33
  15. package/dist/checks/bin/check-closes-n.js +403 -16
  16. package/dist/checks/bin/check-coherence.js +410 -16
  17. package/dist/checks/bin/check-dead-branch-push.js +369 -16
  18. package/dist/checks/bin/check-dispatch-readiness.js +410 -16
  19. package/dist/checks/bin/check-doc-coverage-push.js +670 -30
  20. package/dist/checks/bin/check-doc-coverage.js +672 -32
  21. package/dist/checks/bin/check-doctrine-no-procedures.js +376 -16
  22. package/dist/checks/bin/check-doctrine-portability.js +670 -30
  23. package/dist/checks/bin/check-evidence-fresh.js +762 -113
  24. package/dist/checks/bin/check-exec-bits.js +668 -28
  25. package/dist/checks/bin/check-first-push-dispatch.js +410 -16
  26. package/dist/checks/bin/check-issue-assignment.js +403 -16
  27. package/dist/checks/bin/check-issue-milestone-attach.js +369 -16
  28. package/dist/checks/bin/check-issue-objectives-numbering.js +369 -16
  29. package/dist/checks/bin/check-issue-parts-coverage.js +369 -16
  30. package/dist/checks/bin/check-issue-surface-globs.js +410 -16
  31. package/dist/checks/bin/check-issue-title-grammar.js +369 -16
  32. package/dist/checks/bin/check-issue-tranche-label.js +369 -16
  33. package/dist/checks/bin/check-main-branch-refusal.js +369 -16
  34. package/dist/checks/bin/check-no-disk-state.js +369 -16
  35. package/dist/checks/bin/check-pr-premise-reassert.js +376 -16
  36. package/dist/checks/bin/check-pr-report-density.js +369 -16
  37. package/dist/checks/bin/check-quoted-command.js +671 -31
  38. package/dist/checks/bin/check-reader-resolvable-prose.js +674 -34
  39. package/dist/checks/bin/check-registry-gates.js +369 -16
  40. package/dist/checks/bin/check-retired-vocabulary.js +669 -29
  41. package/dist/checks/bin/check-review-gate.js +391 -16
  42. package/dist/checks/bin/check-single-plan-pr.js +369 -16
  43. package/dist/checks/bin/check-surface-scope.js +403 -16
  44. package/dist/checks/bin/check-test-plan.js +369 -16
  45. package/dist/checks/bin/check-token-collection-wired.js +369 -16
  46. package/dist/checks/bin/check-token-report.js +382 -19
  47. package/dist/checks/bin/check-workspace-escape.js +668 -28
  48. package/dist/index.js +15307 -10932
  49. package/dist/lib/pre-push-select-tests.js +154 -11
  50. package/package.json +1 -1
  51. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  52. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  53. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  54. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error/page.js +2 -1
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js +8 -7
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js +8 -7
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js +9 -8
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js +9 -8
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js +8 -7
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +16 -4
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js +10 -8
  85. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  86. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  88. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js +8 -7
  89. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  90. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  91. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  92. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js +8 -7
  93. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  94. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  95. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  96. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js +9 -8
  97. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  98. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  99. 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
  100. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__08f5pba._.js +3 -0
  101. 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
  102. 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
  103. 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
  104. 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
  105. 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
  106. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1-zcufa._.js +51 -0
  107. 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
  108. 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
  109. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1bh2awk._.js +3 -0
  110. 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
  111. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1qmxez3._.js +4 -0
  112. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1r47dr7._.js +28 -0
  113. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  114. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_01fv4fj._.js +1 -1
  115. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_0gvm3og._.js → _096f46i._.js} +3 -2
  116. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_0lwxg63._.js → _0af2one._.js} +3 -2
  117. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_1n0cnq-._.js → _0zf4d27._.js} +45 -2
  118. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_03x_w6q._.js → _1-wo68i._.js} +3 -2
  119. 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
  120. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1wdusod._.js +3 -0
  121. 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
  122. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_next_1khj-jh._.js +3 -0
  123. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  124. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  125. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  126. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +17 -5
  127. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3_9xytdmiv1xi.js → 25f3-jtiv8p5n.js} +4 -4
  128. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0bn8c8v5q429o.js → 27-6ym7lp2v8o.js} +1 -1
  129. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3-6a3tinbdb-7.js → 3daxns3o--9me.js} +1 -1
  130. package/studio-standalone/apps/vinaya-studio/web/tsconfig.json +12 -0
  131. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0z6dav-._.js +0 -51
  132. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__13rrmhr._.js +0 -3
  133. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__17h4sy8._.js +0 -45
  134. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +0 -3
  135. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_next_17sz44y._.js +0 -3
  136. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_buildManifest.js +0 -0
  137. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_clientMiddlewareManifest.js +0 -0
  138. /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-exec-bits.ts
6
- import { execFileSync as execFileSync6 } from "node:child_process";
7
- import { readFileSync as readFileSync3 } from "node:fs";
6
+ import { execFileSync as execFileSync7 } from "node:child_process";
7
+ import { readFileSync as readFileSync4 } from "node:fs";
8
8
 
9
9
  // src/checks/contract.ts
10
10
  var CHECK_SCHEMA_VERSION = 1;
@@ -14,8 +14,8 @@ function emitCheckError(error) {
14
14
  }
15
15
 
16
16
  // src/lib/diff-evidence.ts
17
- import { execFileSync as execFileSync5 } from "node:child_process";
18
- import { join as join4, relative, resolve as resolve2 } from "node:path";
17
+ import { execFileSync as execFileSync6 } from "node:child_process";
18
+ import { join as join5, relative, resolve as resolve2 } from "node:path";
19
19
  // ../../packages/aeg-core/src/gate-audience.ts
20
20
  var GATE_AUDIENCE = {
21
21
  "check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
@@ -1656,6 +1656,52 @@ var systemEnv3 = {
1656
1656
  ...process.env,
1657
1657
  PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].filter(Boolean).join(":")
1658
1658
  };
1659
+ var defaultExec = () => execFileAsync3("git", ["remote", "get-url", "origin"], { env: systemEnv3, timeout: 5000 });
1660
+ var cached;
1661
+ var inflight;
1662
+ async function resolveRepo(exec = defaultExec) {
1663
+ if (cached !== undefined)
1664
+ return cached;
1665
+ const fromEnv = process.env.AEG_REPO;
1666
+ if (fromEnv) {
1667
+ const parsed = parseOwnerRepo(fromEnv);
1668
+ if (parsed) {
1669
+ cached = parsed;
1670
+ return cached;
1671
+ }
1672
+ }
1673
+ if (inflight)
1674
+ return inflight;
1675
+ inflight = (async () => {
1676
+ try {
1677
+ const { stdout } = await exec();
1678
+ cached = parseGitRemoteUrl(stdout.trim());
1679
+ return cached;
1680
+ } catch (err) {
1681
+ const message = err instanceof Error ? err.message : String(err);
1682
+ console.warn(`[aeg-forge-state] resolveRepo: git remote lookup failed (will retry): ${message}`);
1683
+ return null;
1684
+ } finally {
1685
+ inflight = undefined;
1686
+ }
1687
+ })();
1688
+ return inflight;
1689
+ }
1690
+ function parseGitRemoteUrl(url) {
1691
+ const ssh = url.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
1692
+ if (ssh?.[1] && ssh[2])
1693
+ return { owner: ssh[1], repo: ssh[2] };
1694
+ const https = url.match(/^https?:\/\/(?:[^@]+@)?github\.com\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
1695
+ if (https?.[1] && https[2])
1696
+ return { owner: https[1], repo: https[2] };
1697
+ return null;
1698
+ }
1699
+ function parseOwnerRepo(value) {
1700
+ const match = value.match(/^([^/]+)\/(.+)$/);
1701
+ if (!match?.[1] || !match[2])
1702
+ return null;
1703
+ return { owner: match[1], repo: match[2] };
1704
+ }
1659
1705
  // ../../packages/aeg-core/src/anchored-region.ts
1660
1706
  function anchoredRegionBounds(body, field) {
1661
1707
  const masked = maskCode(body);
@@ -1968,6 +2014,7 @@ var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\
1968
2014
  var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
1969
2015
  var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1970
2016
  var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
2017
+ var BASE_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged base:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
1971
2018
  function firstFiveLines(comment) {
1972
2019
  return comment.split(`
1973
2020
  `).slice(0, 5).join(`
@@ -2003,6 +2050,15 @@ function extractPolicyDigest(comment) {
2003
2050
  const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
2004
2051
  return m ? m[1].toLowerCase() : null;
2005
2052
  }
2053
+ function firstThirteenLines(comment) {
2054
+ return comment.split(`
2055
+ `).slice(0, 13).join(`
2056
+ `);
2057
+ }
2058
+ function extractBaseSha(comment) {
2059
+ const m = firstThirteenLines(comment).match(BASE_SHA_PATTERN);
2060
+ return m ? m[1].toLowerCase() : null;
2061
+ }
2006
2062
  var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
2007
2063
  function extractFindingSeverities(comment) {
2008
2064
  return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
@@ -2020,6 +2076,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2020
2076
  rulingOrdinal: null,
2021
2077
  briefHash: null,
2022
2078
  policyDigest: null,
2079
+ baseSha: null,
2023
2080
  findingSeverities: [],
2024
2081
  danglingNote: `no ${missingLabel} verdict comment found on this PR`
2025
2082
  };
@@ -2034,6 +2091,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2034
2091
  rulingOrdinal: null,
2035
2092
  briefHash: null,
2036
2093
  policyDigest: null,
2094
+ baseSha: null,
2037
2095
  findingSeverities: [],
2038
2096
  danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
2039
2097
  };
@@ -2045,6 +2103,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2045
2103
  rulingOrdinal: extractRulingOrdinal(latest),
2046
2104
  briefHash: extractBriefHash(latest),
2047
2105
  policyDigest: extractPolicyDigest(latest),
2106
+ baseSha: extractBaseSha(latest),
2048
2107
  findingSeverities: extractFindingSeverities(latest),
2049
2108
  danglingNote: null
2050
2109
  };
@@ -3958,7 +4017,11 @@ function briefHash(brief) {
3958
4017
  `).digest("hex");
3959
4018
  }
3960
4019
  function policyDigest(policy) {
3961
- return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
4020
+ return createHash4("sha256").update(JSON.stringify({
4021
+ codeReviewThreshold: policy.codeReviewThreshold,
4022
+ securityThreshold: policy.securityThreshold,
4023
+ maxRounds: policy.maxRounds
4024
+ })).digest("hex");
3962
4025
  }
3963
4026
  function isBoundToHead(echoed, headSha) {
3964
4027
  if (!echoed.headSha)
@@ -3974,8 +4037,12 @@ function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
3974
4037
  return false;
3975
4038
  return judged === current;
3976
4039
  }
3977
- function isBoundToPatch(echoed, headSha, patchIdOf) {
3978
- return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
4040
+ function isBoundToBase(echoed, currentBase) {
4041
+ if (currentBase === null)
4042
+ return true;
4043
+ if (!echoed.baseSha)
4044
+ return false;
4045
+ return currentBase.toLowerCase().startsWith(echoed.baseSha.toLowerCase());
3979
4046
  }
3980
4047
  function isBoundToObjectives(echoed, currentVersion) {
3981
4048
  if (currentVersion === null)
@@ -3996,14 +4063,18 @@ function isBoundToPolicy(echoed, currentDigest) {
3996
4063
  return echoed.policyDigest === currentDigest;
3997
4064
  }
3998
4065
  function compareManifest(echoed, current, patchIdOf) {
3999
- const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
4066
+ const exactHead = isBoundToHead(echoed, current.headSha);
4067
+ const patchHead = exactHead ? false : isBoundByPatchIdentity(echoed, current.headSha, patchIdOf);
4068
+ const head = exactHead || patchHead;
4069
+ const base = patchHead ? true : isBoundToBase(echoed, current.baseSha);
4000
4070
  const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
4001
4071
  const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
4002
4072
  const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
4003
4073
  const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
4004
4074
  return {
4005
- bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
4075
+ bound: head && base && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
4006
4076
  head,
4077
+ base,
4007
4078
  briefHash: briefHashBound,
4008
4079
  objectivesVersion: objectivesVersion2,
4009
4080
  rulingOrdinal,
@@ -4055,6 +4126,7 @@ function checkReviewGate(input) {
4055
4126
  const securityClean = securityTextClean && securityPolicyClean;
4056
4127
  const currentManifest = {
4057
4128
  headSha: input.headSha,
4129
+ baseSha: input.baseSha ?? null,
4058
4130
  briefHash: input.briefHash ?? null,
4059
4131
  objectivesVersion: input.objectivesVersion,
4060
4132
  rulingOrdinal: input.rulingOrdinal,
@@ -4062,6 +4134,7 @@ function checkReviewGate(input) {
4062
4134
  };
4063
4135
  const codeReviewEchoed = {
4064
4136
  headSha: codeReview.headSha,
4137
+ baseSha: codeReview.baseSha,
4065
4138
  briefHash: codeReview.briefHash,
4066
4139
  objectivesVersion: codeReview.objectivesVersion,
4067
4140
  rulingOrdinal: codeReview.rulingOrdinal,
@@ -4069,6 +4142,7 @@ function checkReviewGate(input) {
4069
4142
  };
4070
4143
  const securityEchoed = {
4071
4144
  headSha: security.headSha,
4145
+ baseSha: security.baseSha,
4072
4146
  briefHash: security.briefHash,
4073
4147
  objectivesVersion: security.objectivesVersion,
4074
4148
  rulingOrdinal: security.rulingOrdinal,
@@ -4096,6 +4170,8 @@ function checkReviewGate(input) {
4096
4170
  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`);
4097
4171
  } else if (!codeReviewBound) {
4098
4172
  problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
4173
+ } else if (!codeReviewBinding.base) {
4174
+ 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)`);
4099
4175
  } else if (!codeReviewObjectivesBound) {
4100
4176
  problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
4101
4177
  } else if (!codeReviewRulingsBound) {
@@ -4111,6 +4187,8 @@ function checkReviewGate(input) {
4111
4187
  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`);
4112
4188
  } else if (!securityBound) {
4113
4189
  problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
4190
+ } else if (!securityBinding.base) {
4191
+ 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)`);
4114
4192
  } else if (!securityObjectivesBound) {
4115
4193
  problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
4116
4194
  } else if (!securityRulingsBound) {
@@ -4968,8 +5046,8 @@ var TaskToolErrorSchema = z.object({
4968
5046
  detail: z.string().optional()
4969
5047
  });
4970
5048
  var TaskToolRefSchema = z.union([
4971
- z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
4972
- z.object({ issue: z.number().int().positive() })
5049
+ z.object({ tranche: z.string().min(1), id: z.string().min(1) }).strict(),
5050
+ z.object({ issue: z.number().int().positive() }).strict()
4973
5051
  ]);
4974
5052
  var MAX_PAGE_LIMIT = 100;
4975
5053
  var PageRequestSchema = z.object({
@@ -4983,7 +5061,7 @@ var ObservedSchema = z.object({
4983
5061
  });
4984
5062
  var TaskStatusInputSchema = z.object({
4985
5063
  task: TaskToolRefSchema.optional()
4986
- }).merge(PageRequestSchema);
5064
+ }).merge(PageRequestSchema).strict();
4987
5065
  var TaskStatusItemSchema = z.object({
4988
5066
  task: TaskToolRefSchema,
4989
5067
  issue: z.number().int().positive(),
@@ -5007,9 +5085,20 @@ var EscalationEvidenceSchema = z.object({
5007
5085
  reviewer: z.string().nullable(),
5008
5086
  security: z.string().nullable()
5009
5087
  });
5088
+ var EscalationRunIdentitySchema = z.object({
5089
+ runId: z.string(),
5090
+ pid: z.number().int().positive(),
5091
+ host: z.string()
5092
+ });
5093
+ var EscalationInputVersionsSchema = z.object({
5094
+ briefHash: z.string().nullable(),
5095
+ objectivesVersion: z.string().nullable(),
5096
+ rulingOrdinal: z.number().int().nonnegative(),
5097
+ policyDigest: z.string()
5098
+ });
5010
5099
  var TaskEscalationReadInputSchema = z.object({
5011
5100
  task: TaskToolRefSchema
5012
- }).merge(PageRequestSchema);
5101
+ }).merge(PageRequestSchema).strict();
5013
5102
  var TaskEscalationPacketSchema = z.object({
5014
5103
  reason: z.string(),
5015
5104
  detail: z.string().nullable(),
@@ -5017,7 +5106,9 @@ var TaskEscalationPacketSchema = z.object({
5017
5106
  evidence: EscalationEvidenceSchema.nullable(),
5018
5107
  attemptedRecovery: z.string(),
5019
5108
  requestedAuthority: RequestedAuthoritySchema,
5020
- permittedNextActions: z.array(z.string())
5109
+ permittedNextActions: z.array(z.string()),
5110
+ runIdentity: EscalationRunIdentitySchema.nullable(),
5111
+ inputVersions: EscalationInputVersionsSchema.nullable()
5021
5112
  }).merge(ObservedSchema);
5022
5113
  var TaskEscalationReadResultSchema = z.object({
5023
5114
  items: z.array(TaskEscalationPacketSchema),
@@ -5026,15 +5117,48 @@ var TaskEscalationReadResultSchema = z.object({
5026
5117
  var TaskStartInputSchema = z.object({
5027
5118
  tranche: z.string().min(1),
5028
5119
  id: z.string().min(1)
5120
+ }).strict();
5121
+ var TaskStartResultSchema = z.object({
5122
+ requestId: z.string().min(1),
5123
+ run: z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
5124
+ started: z.boolean(),
5125
+ startedAt: z.string(),
5126
+ mode: z.literal("attended")
5029
5127
  });
5030
5128
  var TaskResumeInputSchema = z.object({
5031
5129
  task: TaskToolRefSchema
5032
- });
5130
+ }).strict();
5033
5131
  var TaskCancelInputSchema = z.object({
5034
5132
  task: TaskToolRefSchema,
5035
5133
  reason: z.string().min(1)
5134
+ }).strict();
5135
+ var TaskResumeResultSchema = z.object({
5136
+ task: z.number().int().positive(),
5137
+ pr: z.number().int().positive(),
5138
+ escalationId: z.string().min(1),
5139
+ outcome: z.enum(["started", "already_resumed"]),
5140
+ authenticatedBy: z.string().min(1),
5141
+ authenticatedFrom: z.string().min(1)
5142
+ });
5143
+ var TaskCancelOutcomeSchema = z.enum(["confirmed", "pending", "uncertain"]);
5144
+ var TaskCancelResultSchema = z.object({
5145
+ task: z.number().int().positive(),
5146
+ pr: z.number().int().positive(),
5147
+ escalationId: z.string().min(1),
5148
+ outcome: TaskCancelOutcomeSchema,
5149
+ authenticatedBy: z.string().min(1),
5150
+ authenticatedFrom: z.string().min(1),
5151
+ fencedEffectKeys: z.array(z.string())
5036
5152
  });
5037
- var NoResultSchema = z.never();
5153
+ var TASK_TOOL_NAMES = [
5154
+ "task_start",
5155
+ "task_status",
5156
+ "task_escalation_read",
5157
+ "task_resume",
5158
+ "task_cancel"
5159
+ ];
5160
+ var OPERATOR_STATUS_FOLLOW = "task_status_follow";
5161
+ var OPERATOR_TOOL_GRANT = [...TASK_TOOL_NAMES, OPERATOR_STATUS_FOLLOW];
5038
5162
  // ../../packages/aeg-core/src/first-push-dispatch-gate.ts
5039
5163
  function parseTaskBranch(branch) {
5040
5164
  const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
@@ -5257,6 +5381,9 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
5257
5381
  }
5258
5382
  return findings;
5259
5383
  }
5384
+ // ../../packages/aeg-core/src/log/envelope.ts
5385
+ import { createHash as createHash5 } from "node:crypto";
5386
+
5260
5387
  // ../../packages/aeg-core/src/log/schema.ts
5261
5388
  import { z as z2 } from "zod";
5262
5389
  var ROLE_VALUES = [
@@ -5358,7 +5485,8 @@ var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
5358
5485
  }).strict(),
5359
5486
  z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
5360
5487
  z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
5361
- z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
5488
+ z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict(),
5489
+ z2.object({ type: z2.literal("completed") }).strict()
5362
5490
  ]);
5363
5491
  var dispatchShared = {
5364
5492
  meta: HeaderMetaSchema,
@@ -5418,7 +5546,8 @@ var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
5418
5546
  round: z2.number().int(),
5419
5547
  head: z2.string(),
5420
5548
  all_approve: z2.boolean(),
5421
- blockers: z2.number().int().nonnegative()
5549
+ blockers: z2.number().int().nonnegative(),
5550
+ findings: z2.array(ReviewFindingSchema).optional()
5422
5551
  }).strict(),
5423
5552
  z2.object({
5424
5553
  ...loopShared,
@@ -5597,7 +5726,9 @@ var roleAttemptShared = {
5597
5726
  kind: z2.literal("role_attempt"),
5598
5727
  ...envelopeTail,
5599
5728
  actor: z2.string().nullable(),
5600
- attempt: z2.number().int().nullable()
5729
+ attempt: z2.number().int().nullable(),
5730
+ effect_id: z2.string(),
5731
+ model: z2.string().nullable()
5601
5732
  };
5602
5733
  var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
5603
5734
  z2.object({
@@ -5656,6 +5787,144 @@ var LogEventSchema = z2.union([
5656
5787
  HandoffEventSchema,
5657
5788
  EffectEventSchema
5658
5789
  ]);
5790
+
5791
+ // ../../packages/aeg-core/src/log/envelope.ts
5792
+ function isRole(value) {
5793
+ return value !== undefined && ROLE_VALUES.includes(value);
5794
+ }
5795
+ function actorIdFromEnv(role) {
5796
+ return role === undefined || role === "" ? null : role;
5797
+ }
5798
+ function lineageFieldFromEnv(value) {
5799
+ return value === undefined || value === "" ? null : value;
5800
+ }
5801
+ function attemptFromEnv(value) {
5802
+ if (value === undefined || value === "")
5803
+ return null;
5804
+ const n = Number(value);
5805
+ return Number.isInteger(n) ? n : null;
5806
+ }
5807
+ function provenanceFor(input) {
5808
+ if (input.provenance)
5809
+ return input.provenance;
5810
+ if (input.env.role || input.env.task)
5811
+ return "env_correlated";
5812
+ return "unavailable";
5813
+ }
5814
+ function issueFromTask(task) {
5815
+ if (task === undefined || task === "")
5816
+ return null;
5817
+ const n = Number(task);
5818
+ return Number.isInteger(n) ? n : null;
5819
+ }
5820
+ function roundFromEnv(round) {
5821
+ if (round === undefined || round === "")
5822
+ return;
5823
+ const n = Number(round);
5824
+ return Number.isInteger(n) ? n : undefined;
5825
+ }
5826
+ function buildHeader(input) {
5827
+ const role = isRole(input.env.role) ? input.env.role : "unattributed";
5828
+ const round = roundFromEnv(input.env.round);
5829
+ const subject = {
5830
+ issue: issueFromTask(input.env.task),
5831
+ role,
5832
+ ...round !== undefined ? { round } : {},
5833
+ ...input.subject ?? {}
5834
+ };
5835
+ return {
5836
+ meta: {
5837
+ schema: 2,
5838
+ ts: input.now.toISOString(),
5839
+ run_id: input.runId,
5840
+ seq: input.seq,
5841
+ repo: input.repo,
5842
+ vinaya: input.vinaya,
5843
+ doctrine: input.doctrine,
5844
+ host: input.host,
5845
+ machine: createHash5("sha256").update(input.hostname).digest("hex"),
5846
+ event_id: input.eventId,
5847
+ process_id: input.processId,
5848
+ actor_id: actorIdFromEnv(input.env.role),
5849
+ lineage: {
5850
+ run: lineageFieldFromEnv(input.env.run),
5851
+ attempt: attemptFromEnv(input.env.attempt),
5852
+ parent: lineageFieldFromEnv(input.env.parent)
5853
+ },
5854
+ input_versions: {
5855
+ objectives_version: input.inputVersions?.objectivesVersion ?? null,
5856
+ brief_hash: input.inputVersions?.briefHash ?? null,
5857
+ ruling_ordinal: input.inputVersions?.rulingOrdinal ?? null,
5858
+ policy_digest: input.inputVersions?.policyDigest ?? null
5859
+ },
5860
+ provenance: provenanceFor(input)
5861
+ },
5862
+ subject
5863
+ };
5864
+ }
5865
+ // ../../packages/aeg-core/src/log/redact.ts
5866
+ var GITHUB_TOKEN_PATTERN = /\b(?:gho_|ghp_|ghs_|ghu_|ghr_|github_pat_)[A-Za-z0-9_]+\b/g;
5867
+ var CLASSIC_GITHUB_PAT_PATTERN = /\b((?:token|GITHUB_TOKEN|PAT|api[_-]?key|api[_-]?token)\s*[:=]\s*)[a-f0-9]{40}\b/gi;
5868
+ var BEARER_TOKEN_PATTERN = /\bBearer\s+\S+/g;
5869
+ var AWS_ACCESS_KEY_PATTERN = /\bAKIA[0-9A-Z]{16}\b/g;
5870
+ var SLACK_TOKEN_PATTERN = /\bxox[baprs]-[0-9A-Za-z-]+\b/g;
5871
+ var STRIPE_KEY_PATTERN = /\b(?:sk|pk|rk)_(?:live|test)_[0-9A-Za-z]{10,}\b/g;
5872
+ var ANTHROPIC_KEY_PATTERN = /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g;
5873
+ var OPENAI_KEY_PATTERN = /\bsk-(?!ant-)[A-Za-z0-9]{20,}\b/g;
5874
+ var NPM_TOKEN_PATTERN = /\bnpm_[A-Za-z0-9]{36}\b/g;
5875
+ var JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
5876
+ var PEM_BLOCK_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
5877
+ var BASIC_AUTH_URL_PATTERN = /(https?:\/\/)[^/\s:@]+:[^/\s@]+@/g;
5878
+ 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;
5879
+ function redactString(value, home) {
5880
+ 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>");
5881
+ if (home.length > 0 && out.startsWith(home)) {
5882
+ const rest = out.slice(home.length);
5883
+ if (rest === "" || rest.startsWith("/"))
5884
+ out = `~${rest}`;
5885
+ }
5886
+ return out;
5887
+ }
5888
+ function redact(value, home) {
5889
+ if (typeof value === "string")
5890
+ return redactString(value, home);
5891
+ if (Array.isArray(value))
5892
+ return value.map((v) => redact(v, home));
5893
+ if (value !== null && typeof value === "object") {
5894
+ const out = {};
5895
+ for (const [k, v] of Object.entries(value)) {
5896
+ out[k] = redact(v, home);
5897
+ }
5898
+ return out;
5899
+ }
5900
+ return value;
5901
+ }
5902
+ // ../../packages/aeg-core/src/log/store.ts
5903
+ function metaOf(obj) {
5904
+ if (obj === null || typeof obj !== "object")
5905
+ return null;
5906
+ const meta = obj.meta;
5907
+ if (meta === null || typeof meta !== "object")
5908
+ return null;
5909
+ return meta;
5910
+ }
5911
+ function recordIdentity(obj) {
5912
+ const meta = metaOf(obj);
5913
+ if (meta === null)
5914
+ return null;
5915
+ const eventId = meta.event_id;
5916
+ if (typeof eventId === "string" && eventId.length > 0)
5917
+ return eventId;
5918
+ const runId = meta.run_id;
5919
+ const seq = meta.seq;
5920
+ if (typeof runId === "string" && runId.length > 0 && typeof seq === "number" && Number.isInteger(seq)) {
5921
+ return `${runId}:${seq}`;
5922
+ }
5923
+ return null;
5924
+ }
5925
+
5926
+ // ../../packages/aeg-core/src/log/artifact.ts
5927
+ var TASK_LOG_ARTIFACT_MAX_BYTES = 8 * 1024 * 1024;
5659
5928
  // ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
5660
5929
  var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
5661
5930
  // ../../packages/aeg-core/src/control-store/records.ts
@@ -5670,7 +5939,9 @@ var RunRecordSchema = z3.object({
5670
5939
  runId: z3.string().min(1),
5671
5940
  pid: z3.number().int().positive(),
5672
5941
  host: z3.string().min(1),
5673
- startedAt: isoTimestamp
5942
+ startedAt: isoTimestamp,
5943
+ childStartedAt: z3.string().min(1).nullable().optional(),
5944
+ childCommand: z3.string().min(1).nullable().optional()
5674
5945
  }).strict();
5675
5946
  var InputRecordSchema = z3.object({
5676
5947
  version: z3.literal(1),
@@ -5703,6 +5974,88 @@ var TransitionRecordSchema = z3.object({
5703
5974
  detail: z3.string().optional(),
5704
5975
  at: isoTimestamp
5705
5976
  }).strict();
5977
+ var ManifestRecordSchema = z3.object({
5978
+ version: z3.literal(1),
5979
+ kind: z3.literal("manifest"),
5980
+ task: taskId,
5981
+ round: epochNumber,
5982
+ repository: z3.string().min(1),
5983
+ pr: z3.number().int().positive(),
5984
+ branch: z3.string().min(1),
5985
+ baseSha: z3.string().min(1).nullable(),
5986
+ headSha: z3.string().min(1),
5987
+ briefHash: z3.string().min(1).nullable(),
5988
+ objectivesVersion: z3.string().min(1).nullable(),
5989
+ rulingOrdinal: epochNumber,
5990
+ policyDigest: z3.string().min(1),
5991
+ recordedAt: isoTimestamp
5992
+ }).strict();
5993
+ var roundHeadIdentity = z3.object({ round: z3.number().int().positive(), head: z3.string().min(1) }).strict();
5994
+ var LoopBudgetsSchema = z3.object({
5995
+ mechanicalRetries: z3.number().int().nonnegative(),
5996
+ reviewRounds: z3.number().int().nonnegative(),
5997
+ infrastructureRetries: z3.number().int().nonnegative()
5998
+ }).strict();
5999
+ var LoopStateRecordSchema = z3.object({
6000
+ version: z3.literal(1),
6001
+ kind: z3.literal("loop_state"),
6002
+ task: taskId,
6003
+ round: z3.number().int().positive(),
6004
+ phase: z3.string().min(1),
6005
+ pauseReason: z3.string().min(1).nullable(),
6006
+ budgets: LoopBudgetsSchema,
6007
+ heldResult: roundHeadIdentity.nullable(),
6008
+ deliveredFindings: roundHeadIdentity.nullable(),
6009
+ recordedAt: isoTimestamp
6010
+ }).strict();
6011
+ var EffectRecordSchema = z3.object({
6012
+ version: z3.literal(1),
6013
+ kind: z3.literal("effect"),
6014
+ task: taskId,
6015
+ key: z3.string().min(1),
6016
+ operation: z3.string().min(1),
6017
+ target: z3.string().min(1),
6018
+ inputVersion: z3.number().int().nonnegative(),
6019
+ payloadDigest: z3.string().min(1),
6020
+ status: z3.union([z3.literal("started"), z3.literal("verified"), z3.literal("uncertain")]),
6021
+ url: z3.string().optional(),
6022
+ recordedAt: isoTimestamp
6023
+ }).strict();
6024
+ var EscalationRecordSchema = z3.object({
6025
+ version: z3.literal(1),
6026
+ kind: z3.literal("escalation"),
6027
+ task: taskId,
6028
+ escalationId: z3.string().min(1),
6029
+ round: epochNumber,
6030
+ head: z3.string().min(1),
6031
+ branch: z3.string().min(1),
6032
+ pr: z3.number().int().positive().nullable(),
6033
+ runId: z3.string().min(1),
6034
+ pid: z3.number().int().positive(),
6035
+ host: z3.string().min(1),
6036
+ agent: z3.string().min(1).optional(),
6037
+ reason: z3.string().min(1),
6038
+ detail: z3.string().optional(),
6039
+ evidence: z3.string().optional(),
6040
+ attemptedRecovery: z3.string().min(1),
6041
+ requestedDecision: z3.string().min(1),
6042
+ recipient: z3.string().min(1),
6043
+ briefHash: z3.string().min(1).nullable(),
6044
+ objectivesVersion: z3.string().min(1).nullable(),
6045
+ rulingOrdinal: epochNumber,
6046
+ policyDigest: z3.string().min(1),
6047
+ recordedAt: isoTimestamp
6048
+ }).strict();
6049
+ var ResolutionRecordSchema = z3.object({
6050
+ version: z3.literal(1),
6051
+ kind: z3.literal("resolution"),
6052
+ task: taskId,
6053
+ escalationId: z3.string().min(1),
6054
+ decision: z3.union([z3.literal("resume"), z3.literal("cancel")]),
6055
+ authenticatedBy: z3.string().min(1),
6056
+ authenticatedFrom: z3.string().min(1),
6057
+ consumedAt: isoTimestamp
6058
+ }).strict();
5706
6059
  // src/lib/config.ts
5707
6060
  import { execFileSync as execFileSync2 } from "node:child_process";
5708
6061
  import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
@@ -5883,6 +6236,13 @@ var VinayaConfigSchema = z4.object({
5883
6236
  message: "must be at most 3600000 (1 hour)"
5884
6237
  });
5885
6238
  }
6239
+ }).optional(),
6240
+ logPublish: z4.object({
6241
+ issue: z4.number().int().positive().optional(),
6242
+ pr: z4.number().int().positive().optional(),
6243
+ maxChunksPerFlush: z4.number().int().positive().optional()
6244
+ }).refine((v) => !(v.issue !== undefined && v.pr !== undefined), {
6245
+ message: "logPublish: set at most one of issue/pr, not both"
5886
6246
  }).optional()
5887
6247
  });
5888
6248
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
@@ -6230,6 +6590,16 @@ var COMMANDS = [
6230
6590
  ],
6231
6591
  status: "shipped"
6232
6592
  },
6593
+ {
6594
+ name: "task-tools serve",
6595
+ description: "Run the task-operator MCP tool server over stdio — the command both runtime adapters register",
6596
+ details: [
6597
+ "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.",
6598
+ "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.",
6599
+ "`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."
6600
+ ],
6601
+ status: "shipped"
6602
+ },
6233
6603
  {
6234
6604
  name: "pr create",
6235
6605
  description: "Open a pull request after full brief-schema validation",
@@ -6374,6 +6744,30 @@ var COMMANDS = [
6374
6744
  ],
6375
6745
  status: "shipped"
6376
6746
  },
6747
+ {
6748
+ name: "log export-artifact",
6749
+ description: "Copy this run's own local outbox into a file, for the generated task-path workflow's own upload step",
6750
+ flags: [],
6751
+ details: [
6752
+ "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.",
6753
+ "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."
6754
+ ],
6755
+ status: "shipped"
6756
+ },
6757
+ {
6758
+ name: "log collect-artifact",
6759
+ description: "Validate a downloaded task-log artifact and publish the valid records through the existing flush path",
6760
+ flags: [
6761
+ { flag: "--pr", description: "The pull request this artifact belongs to" },
6762
+ { flag: "--repo", description: "Expected `owner/repo` — a provenance cross-check against each record" }
6763
+ ],
6764
+ details: [
6765
+ "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.",
6766
+ "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.",
6767
+ "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."
6768
+ ],
6769
+ status: "shipped"
6770
+ },
6377
6771
  {
6378
6772
  name: "milestone create",
6379
6773
  description: "Create a GitHub Milestone from a validated body",
@@ -7436,8 +7830,254 @@ function coreCheckRegistry() {
7436
7830
  }
7437
7831
  var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
7438
7832
 
7833
+ // src/lib/log-sink.ts
7834
+ import { execFileSync as execFileSync5 } from "node:child_process";
7835
+ import { randomUUID } from "node:crypto";
7836
+ import {
7837
+ closeSync as closeSync2,
7838
+ constants as fsConstants2,
7839
+ existsSync as existsSync4,
7840
+ fstatSync as fstatSync2,
7841
+ mkdirSync as mkdirSync2,
7842
+ openSync as openSync2,
7843
+ readFileSync as readFileSync3,
7844
+ renameSync,
7845
+ writeSync
7846
+ } from "node:fs";
7847
+ import { hostname as osHostname, homedir as homedir2 } from "node:os";
7848
+ import { dirname as dirname3, join as join4 } from "node:path";
7849
+ var OUTBOX_MAX_BYTES = 8 * 1024 * 1024;
7850
+ function isEnoent(err) {
7851
+ return typeof err === "object" && err !== null && err.code === "ENOENT";
7852
+ }
7853
+ function resolveDoctrine(cwd, vinayaVersion) {
7854
+ let toplevel;
7855
+ try {
7856
+ toplevel = execFileSync5("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
7857
+ encoding: "utf8",
7858
+ stdio: ["ignore", "pipe", "pipe"]
7859
+ }).trim();
7860
+ } catch (err) {
7861
+ return isEnoent(err) ? "unknown" : vinayaVersion;
7862
+ }
7863
+ if (!existsSync4(join4(toplevel, "aeg-root", "roles")))
7864
+ return vinayaVersion;
7865
+ try {
7866
+ const sha = execFileSync5("git", ["-C", toplevel, "rev-parse", "HEAD:aeg-root"], {
7867
+ encoding: "utf8",
7868
+ stdio: ["ignore", "pipe", "pipe"]
7869
+ }).trim();
7870
+ return `aeg-root@${sha}`;
7871
+ } catch {
7872
+ return vinayaVersion;
7873
+ }
7874
+ }
7875
+ function readVinayaVersion() {
7876
+ try {
7877
+ const pkg = JSON.parse(readFileSync3(join4(packageRoot(import.meta.url), "package.json"), "utf8"));
7878
+ return pkg.version ?? "unknown";
7879
+ } catch {
7880
+ return "unknown";
7881
+ }
7882
+ }
7883
+ var SAFE_PATH_SEGMENT = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
7884
+ function isSafeRepoSegment(segment) {
7885
+ return SAFE_PATH_SEGMENT.test(segment) && !segment.includes("..");
7886
+ }
7887
+ function outboxPathFor(deps, repo, issue) {
7888
+ const dirName = repo ? `${repo.owner}-${repo.repo}` : "unresolved";
7889
+ const fileName = `${issue ?? "none"}.ndjson`;
7890
+ return join4(deps.outboxRoot(), dirName, fileName);
7891
+ }
7892
+ function hostFromEnv(env) {
7893
+ if (env.GITHUB_ACTIONS)
7894
+ return "ci";
7895
+ if (env.VINAYA_HOST === "hook")
7896
+ return "hook";
7897
+ if (env.VINAYA_HOST === "loop")
7898
+ return "loop";
7899
+ return "cli";
7900
+ }
7901
+ function defaultDeps() {
7902
+ return {
7903
+ outboxRoot: () => join4(GLOBAL_VINAYA_HOME, "outbox"),
7904
+ home: () => homedir2(),
7905
+ hostname: () => osHostname(),
7906
+ cwd: () => process.cwd(),
7907
+ now: () => new Date,
7908
+ env: () => process.env,
7909
+ resolveRepo: () => resolveRepo(),
7910
+ vinayaVersion: () => readVinayaVersion(),
7911
+ stderr: (message) => {
7912
+ process.stderr.write(message);
7913
+ },
7914
+ inputVersions: () => {
7915
+ return;
7916
+ }
7917
+ };
7918
+ }
7919
+ var APPEND_OPEN_FLAGS = fsConstants2.O_WRONLY | fsConstants2.O_APPEND | fsConstants2.O_CREAT | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK;
7920
+ function guardedAppendOpen(path) {
7921
+ try {
7922
+ return openSync2(path, APPEND_OPEN_FLAGS, 384);
7923
+ } catch {
7924
+ return;
7925
+ }
7926
+ }
7927
+ function reportRotationOverflow(backupPath, warn) {
7928
+ let raw;
7929
+ try {
7930
+ raw = readFileSync3(backupPath, "utf8");
7931
+ } catch {
7932
+ return;
7933
+ }
7934
+ const lines = raw.split(`
7935
+ `).filter((l) => l.length > 0);
7936
+ if (lines.length === 0)
7937
+ return;
7938
+ const droppedIdentities = lines.map((line, i) => {
7939
+ try {
7940
+ return recordIdentity(JSON.parse(line)) ?? `unreadable:${i}`;
7941
+ } catch {
7942
+ return `unreadable:${i}`;
7943
+ }
7944
+ });
7945
+ const diagnostic = { reason: "capacity", dropped: droppedIdentities.length, droppedIdentities };
7946
+ const shown = diagnostic.droppedIdentities.slice(0, 20);
7947
+ const more = diagnostic.dropped > shown.length ? `, +${diagnostic.dropped - shown.length} more` : "";
7948
+ warn(`vinaya: log outbox rotation is overwriting ${backupPath} — ${diagnostic.dropped} record(s) permanently lost: ${shown.join(", ")}${more}
7949
+ `);
7950
+ }
7951
+ function appendLine(path, line, warn) {
7952
+ try {
7953
+ mkdirSync2(dirname3(path), { recursive: true, mode: 448 });
7954
+ let fd = guardedAppendOpen(path);
7955
+ if (fd === undefined) {
7956
+ warn(`vinaya: log outbox target could not be opened (symlink, FIFO, or unwritable) — refusing: ${path}
7957
+ `);
7958
+ return;
7959
+ }
7960
+ let closed = false;
7961
+ try {
7962
+ const stat = fstatSync2(fd);
7963
+ if (!stat.isFile()) {
7964
+ warn(`vinaya: log outbox target is not a regular file — refusing to write: ${path}
7965
+ `);
7966
+ return;
7967
+ }
7968
+ if (stat.size > OUTBOX_MAX_BYTES) {
7969
+ closeSync2(fd);
7970
+ closed = true;
7971
+ const backupPath = path.replace(/\.ndjson$/, ".1.ndjson");
7972
+ reportRotationOverflow(backupPath, warn);
7973
+ renameSync(path, backupPath);
7974
+ const rotated = guardedAppendOpen(path);
7975
+ if (rotated === undefined) {
7976
+ warn(`vinaya: log outbox target could not be reopened after rotation — refusing: ${path}
7977
+ `);
7978
+ return;
7979
+ }
7980
+ fd = rotated;
7981
+ closed = false;
7982
+ }
7983
+ writeSync(fd, line);
7984
+ } finally {
7985
+ if (!closed)
7986
+ closeSync2(fd);
7987
+ }
7988
+ } catch (err) {
7989
+ warn(`vinaya: log outbox write failed — ${err instanceof Error ? err.message : String(err)}
7990
+ `);
7991
+ }
7992
+ }
7993
+ function createLogSink(overrides = {}) {
7994
+ const deps = { ...defaultDeps(), ...overrides };
7995
+ const runId = deps.env().VINAYA_RUN_ID || randomUUID();
7996
+ const processId = randomUUID();
7997
+ let seq = 0;
7998
+ let warned = false;
7999
+ const warnOnce = (message) => {
8000
+ if (warned)
8001
+ return;
8002
+ warned = true;
8003
+ try {
8004
+ deps.stderr(message);
8005
+ } catch {}
8006
+ };
8007
+ let doctrineCache;
8008
+ const doctrine = () => {
8009
+ if (doctrineCache === undefined)
8010
+ doctrineCache = resolveDoctrine(deps.cwd(), deps.vinayaVersion());
8011
+ return doctrineCache;
8012
+ };
8013
+ let resolveRepoCache;
8014
+ const resolveRepoOnce = () => {
8015
+ if (resolveRepoCache === undefined)
8016
+ resolveRepoCache = deps.resolveRepo();
8017
+ return resolveRepoCache;
8018
+ };
8019
+ function log(e) {
8020
+ try {
8021
+ const env = deps.env();
8022
+ const host = hostFromEnv(env);
8023
+ const now = deps.now();
8024
+ const mySeq = seq++;
8025
+ resolveRepoOnce().then((resolved) => {
8026
+ const repo = resolved && isSafeRepoSegment(resolved.owner) && isSafeRepoSegment(resolved.repo) ? resolved : null;
8027
+ const header = buildHeader({
8028
+ now,
8029
+ runId,
8030
+ seq: mySeq,
8031
+ repo: repo ? `${repo.owner}/${repo.repo}` : null,
8032
+ vinaya: deps.vinayaVersion(),
8033
+ doctrine: doctrine(),
8034
+ host,
8035
+ hostname: deps.hostname(),
8036
+ env: {
8037
+ role: env.VINAYA_ROLE,
8038
+ task: env.VINAYA_TASK,
8039
+ round: env.VINAYA_ROUND,
8040
+ run: env.VINAYA_RUN,
8041
+ attempt: env.VINAYA_ATTEMPT,
8042
+ parent: env.VINAYA_PARENT_EVENT
8043
+ },
8044
+ eventId: randomUUID(),
8045
+ processId,
8046
+ inputVersions: deps.inputVersions()
8047
+ });
8048
+ const full = { ...e, ...header };
8049
+ const parsed = LogEventSchema.safeParse(full);
8050
+ if (!parsed.success) {
8051
+ warnOnce(`vinaya: log() refused an invalid payload — ${parsed.error.issues[0]?.message ?? "schema violation"}
8052
+ `);
8053
+ return;
8054
+ }
8055
+ const line = `${JSON.stringify(redact(parsed.data, deps.home()))}
8056
+ `;
8057
+ appendLine(outboxPathFor(deps, repo, header.subject.issue), line, warnOnce);
8058
+ }).catch((err) => {
8059
+ warnOnce(`vinaya: log() failed — ${err instanceof Error ? err.message : String(err)}
8060
+ `);
8061
+ });
8062
+ } catch (err) {
8063
+ warnOnce(`vinaya: log() failed — ${err instanceof Error ? err.message : String(err)}
8064
+ `);
8065
+ }
8066
+ }
8067
+ const warmup = () => {
8068
+ doctrine();
8069
+ const originalWarn = console.warn;
8070
+ console.warn = () => {};
8071
+ resolveRepoOnce().finally(() => {
8072
+ console.warn = originalWarn;
8073
+ });
8074
+ };
8075
+ return { log, runId, warmup };
8076
+ }
8077
+ var defaultSink = createLogSink();
8078
+
7439
8079
  // src/checks/runner.ts
7440
- var activeKillers = new Set;
8080
+ var activeChecks = new Map;
7441
8081
 
7442
8082
  // src/lib/forge-write.ts
7443
8083
  var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
@@ -7476,7 +8116,7 @@ function parseChangedLineRanges(diffOutput) {
7476
8116
  function fileDiffAgainst(ref, path) {
7477
8117
  let out;
7478
8118
  try {
7479
- out = execFileSync5("git", ["diff", `${ref}...HEAD`, "--", path], {
8119
+ out = execFileSync6("git", ["diff", `${ref}...HEAD`, "--", path], {
7480
8120
  encoding: "utf8",
7481
8121
  stdio: ["ignore", "pipe", "pipe"]
7482
8122
  }).trim();
@@ -7487,7 +8127,7 @@ function fileDiffAgainst(ref, path) {
7487
8127
  }
7488
8128
  function revParse(ref) {
7489
8129
  try {
7490
- return execFileSync5("git", ["rev-parse", "--verify", ref], {
8130
+ return execFileSync6("git", ["rev-parse", "--verify", ref], {
7491
8131
  encoding: "utf8",
7492
8132
  stdio: ["ignore", "pipe", "pipe"]
7493
8133
  }).trim();
@@ -7497,7 +8137,7 @@ function revParse(ref) {
7497
8137
  }
7498
8138
  function repoRoot() {
7499
8139
  try {
7500
- return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
8140
+ return execFileSync6("git", ["rev-parse", "--show-toplevel"], {
7501
8141
  encoding: "utf8",
7502
8142
  stdio: ["ignore", "pipe", "pipe"]
7503
8143
  }).trim();
@@ -7508,7 +8148,7 @@ function repoRoot() {
7508
8148
  function changedFiles(base) {
7509
8149
  let out;
7510
8150
  try {
7511
- out = execFileSync5("git", ["diff", "--name-only", `${base}...HEAD`], {
8151
+ out = execFileSync6("git", ["diff", "--name-only", `${base}...HEAD`], {
7512
8152
  encoding: "utf8",
7513
8153
  stdio: ["ignore", "pipe", "pipe"]
7514
8154
  }).trim();
@@ -7533,7 +8173,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
7533
8173
  const relPaths = changedFiles(ref);
7534
8174
  if (relPaths === null)
7535
8175
  continue;
7536
- return { base: ref, root, files: relPaths.map((p) => join4(root, p)) };
8176
+ return { base: ref, root, files: relPaths.map((p) => join5(root, p)) };
7537
8177
  }
7538
8178
  return null;
7539
8179
  }
@@ -7569,7 +8209,7 @@ var CHECK_NAME = "exec-bits";
7569
8209
  var EXECUTABLE_MODE = "100755";
7570
8210
  function git2(args) {
7571
8211
  try {
7572
- return execFileSync6("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
8212
+ return execFileSync7("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
7573
8213
  } catch {
7574
8214
  return null;
7575
8215
  }
@@ -7583,7 +8223,7 @@ function indexMode(path) {
7583
8223
  }
7584
8224
  function hasShebang(path) {
7585
8225
  try {
7586
- return readFileSync3(path, "utf8").startsWith("#!");
8226
+ return readFileSync4(path, "utf8").startsWith("#!");
7587
8227
  } catch {
7588
8228
  return false;
7589
8229
  }