@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-doc-coverage-push.ts
6
- import { execFileSync as execFileSync6 } from "node:child_process";
7
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
6
+ import { execFileSync as execFileSync7 } from "node:child_process";
7
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
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);
@@ -1957,6 +2003,7 @@ var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\
1957
2003
  var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
1958
2004
  var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1959
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;
1960
2007
  function firstFiveLines(comment) {
1961
2008
  return comment.split(`
1962
2009
  `).slice(0, 5).join(`
@@ -1992,6 +2039,15 @@ function extractPolicyDigest(comment) {
1992
2039
  const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
1993
2040
  return m ? m[1].toLowerCase() : null;
1994
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
+ }
1995
2051
  var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
1996
2052
  function extractFindingSeverities(comment) {
1997
2053
  return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
@@ -2009,6 +2065,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2009
2065
  rulingOrdinal: null,
2010
2066
  briefHash: null,
2011
2067
  policyDigest: null,
2068
+ baseSha: null,
2012
2069
  findingSeverities: [],
2013
2070
  danglingNote: `no ${missingLabel} verdict comment found on this PR`
2014
2071
  };
@@ -2023,6 +2080,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2023
2080
  rulingOrdinal: null,
2024
2081
  briefHash: null,
2025
2082
  policyDigest: null,
2083
+ baseSha: null,
2026
2084
  findingSeverities: [],
2027
2085
  danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
2028
2086
  };
@@ -2034,6 +2092,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2034
2092
  rulingOrdinal: extractRulingOrdinal(latest),
2035
2093
  briefHash: extractBriefHash(latest),
2036
2094
  policyDigest: extractPolicyDigest(latest),
2095
+ baseSha: extractBaseSha(latest),
2037
2096
  findingSeverities: extractFindingSeverities(latest),
2038
2097
  danglingNote: null
2039
2098
  };
@@ -3947,7 +4006,11 @@ function briefHash(brief) {
3947
4006
  `).digest("hex");
3948
4007
  }
3949
4008
  function policyDigest(policy) {
3950
- return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
4009
+ return createHash4("sha256").update(JSON.stringify({
4010
+ codeReviewThreshold: policy.codeReviewThreshold,
4011
+ securityThreshold: policy.securityThreshold,
4012
+ maxRounds: policy.maxRounds
4013
+ })).digest("hex");
3951
4014
  }
3952
4015
  function isBoundToHead(echoed, headSha) {
3953
4016
  if (!echoed.headSha)
@@ -3963,8 +4026,12 @@ function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
3963
4026
  return false;
3964
4027
  return judged === current;
3965
4028
  }
3966
- function isBoundToPatch(echoed, headSha, patchIdOf) {
3967
- return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, 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());
3968
4035
  }
3969
4036
  function isBoundToObjectives(echoed, currentVersion) {
3970
4037
  if (currentVersion === null)
@@ -3985,14 +4052,18 @@ function isBoundToPolicy(echoed, currentDigest) {
3985
4052
  return echoed.policyDigest === currentDigest;
3986
4053
  }
3987
4054
  function compareManifest(echoed, current, patchIdOf) {
3988
- const head = isBoundToPatch(echoed, current.headSha, 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);
3989
4059
  const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
3990
4060
  const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
3991
4061
  const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
3992
4062
  const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
3993
4063
  return {
3994
- bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
4064
+ bound: head && base && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
3995
4065
  head,
4066
+ base,
3996
4067
  briefHash: briefHashBound,
3997
4068
  objectivesVersion: objectivesVersion2,
3998
4069
  rulingOrdinal,
@@ -4044,6 +4115,7 @@ function checkReviewGate(input) {
4044
4115
  const securityClean = securityTextClean && securityPolicyClean;
4045
4116
  const currentManifest = {
4046
4117
  headSha: input.headSha,
4118
+ baseSha: input.baseSha ?? null,
4047
4119
  briefHash: input.briefHash ?? null,
4048
4120
  objectivesVersion: input.objectivesVersion,
4049
4121
  rulingOrdinal: input.rulingOrdinal,
@@ -4051,6 +4123,7 @@ function checkReviewGate(input) {
4051
4123
  };
4052
4124
  const codeReviewEchoed = {
4053
4125
  headSha: codeReview.headSha,
4126
+ baseSha: codeReview.baseSha,
4054
4127
  briefHash: codeReview.briefHash,
4055
4128
  objectivesVersion: codeReview.objectivesVersion,
4056
4129
  rulingOrdinal: codeReview.rulingOrdinal,
@@ -4058,6 +4131,7 @@ function checkReviewGate(input) {
4058
4131
  };
4059
4132
  const securityEchoed = {
4060
4133
  headSha: security.headSha,
4134
+ baseSha: security.baseSha,
4061
4135
  briefHash: security.briefHash,
4062
4136
  objectivesVersion: security.objectivesVersion,
4063
4137
  rulingOrdinal: security.rulingOrdinal,
@@ -4085,6 +4159,8 @@ function checkReviewGate(input) {
4085
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`);
4086
4160
  } else if (!codeReviewBound) {
4087
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)`);
4088
4164
  } else if (!codeReviewObjectivesBound) {
4089
4165
  problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
4090
4166
  } else if (!codeReviewRulingsBound) {
@@ -4100,6 +4176,8 @@ function checkReviewGate(input) {
4100
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`);
4101
4177
  } else if (!securityBound) {
4102
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)`);
4103
4181
  } else if (!securityObjectivesBound) {
4104
4182
  problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
4105
4183
  } else if (!securityRulingsBound) {
@@ -4957,8 +5035,8 @@ var TaskToolErrorSchema = z.object({
4957
5035
  detail: z.string().optional()
4958
5036
  });
4959
5037
  var TaskToolRefSchema = z.union([
4960
- z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
4961
- z.object({ issue: z.number().int().positive() })
5038
+ z.object({ tranche: z.string().min(1), id: z.string().min(1) }).strict(),
5039
+ z.object({ issue: z.number().int().positive() }).strict()
4962
5040
  ]);
4963
5041
  var MAX_PAGE_LIMIT = 100;
4964
5042
  var PageRequestSchema = z.object({
@@ -4972,7 +5050,7 @@ var ObservedSchema = z.object({
4972
5050
  });
4973
5051
  var TaskStatusInputSchema = z.object({
4974
5052
  task: TaskToolRefSchema.optional()
4975
- }).merge(PageRequestSchema);
5053
+ }).merge(PageRequestSchema).strict();
4976
5054
  var TaskStatusItemSchema = z.object({
4977
5055
  task: TaskToolRefSchema,
4978
5056
  issue: z.number().int().positive(),
@@ -4996,9 +5074,20 @@ var EscalationEvidenceSchema = z.object({
4996
5074
  reviewer: z.string().nullable(),
4997
5075
  security: z.string().nullable()
4998
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
+ });
4999
5088
  var TaskEscalationReadInputSchema = z.object({
5000
5089
  task: TaskToolRefSchema
5001
- }).merge(PageRequestSchema);
5090
+ }).merge(PageRequestSchema).strict();
5002
5091
  var TaskEscalationPacketSchema = z.object({
5003
5092
  reason: z.string(),
5004
5093
  detail: z.string().nullable(),
@@ -5006,7 +5095,9 @@ var TaskEscalationPacketSchema = z.object({
5006
5095
  evidence: EscalationEvidenceSchema.nullable(),
5007
5096
  attemptedRecovery: z.string(),
5008
5097
  requestedAuthority: RequestedAuthoritySchema,
5009
- permittedNextActions: z.array(z.string())
5098
+ permittedNextActions: z.array(z.string()),
5099
+ runIdentity: EscalationRunIdentitySchema.nullable(),
5100
+ inputVersions: EscalationInputVersionsSchema.nullable()
5010
5101
  }).merge(ObservedSchema);
5011
5102
  var TaskEscalationReadResultSchema = z.object({
5012
5103
  items: z.array(TaskEscalationPacketSchema),
@@ -5015,15 +5106,48 @@ var TaskEscalationReadResultSchema = z.object({
5015
5106
  var TaskStartInputSchema = z.object({
5016
5107
  tranche: z.string().min(1),
5017
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")
5018
5116
  });
5019
5117
  var TaskResumeInputSchema = z.object({
5020
5118
  task: TaskToolRefSchema
5021
- });
5119
+ }).strict();
5022
5120
  var TaskCancelInputSchema = z.object({
5023
5121
  task: TaskToolRefSchema,
5024
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())
5025
5141
  });
5026
- var NoResultSchema = z.never();
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];
5027
5151
  // ../../packages/aeg-core/src/first-push-dispatch-gate.ts
5028
5152
  function parseTaskBranch(branch) {
5029
5153
  const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
@@ -5246,6 +5370,9 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
5246
5370
  }
5247
5371
  return findings;
5248
5372
  }
5373
+ // ../../packages/aeg-core/src/log/envelope.ts
5374
+ import { createHash as createHash5 } from "node:crypto";
5375
+
5249
5376
  // ../../packages/aeg-core/src/log/schema.ts
5250
5377
  import { z as z2 } from "zod";
5251
5378
  var ROLE_VALUES = [
@@ -5347,7 +5474,8 @@ var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
5347
5474
  }).strict(),
5348
5475
  z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
5349
5476
  z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
5350
- z2.object({ type: z2.literal("archive"), provenance_comment_id: 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()
5351
5479
  ]);
5352
5480
  var dispatchShared = {
5353
5481
  meta: HeaderMetaSchema,
@@ -5407,7 +5535,8 @@ var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
5407
5535
  round: z2.number().int(),
5408
5536
  head: z2.string(),
5409
5537
  all_approve: z2.boolean(),
5410
- blockers: z2.number().int().nonnegative()
5538
+ blockers: z2.number().int().nonnegative(),
5539
+ findings: z2.array(ReviewFindingSchema).optional()
5411
5540
  }).strict(),
5412
5541
  z2.object({
5413
5542
  ...loopShared,
@@ -5586,7 +5715,9 @@ var roleAttemptShared = {
5586
5715
  kind: z2.literal("role_attempt"),
5587
5716
  ...envelopeTail,
5588
5717
  actor: z2.string().nullable(),
5589
- attempt: z2.number().int().nullable()
5718
+ attempt: z2.number().int().nullable(),
5719
+ effect_id: z2.string(),
5720
+ model: z2.string().nullable()
5590
5721
  };
5591
5722
  var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
5592
5723
  z2.object({
@@ -5645,6 +5776,144 @@ var LogEventSchema = z2.union([
5645
5776
  HandoffEventSchema,
5646
5777
  EffectEventSchema
5647
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;
5648
5917
  // ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
5649
5918
  var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
5650
5919
  // ../../packages/aeg-core/src/control-store/records.ts
@@ -5659,7 +5928,9 @@ var RunRecordSchema = z3.object({
5659
5928
  runId: z3.string().min(1),
5660
5929
  pid: z3.number().int().positive(),
5661
5930
  host: z3.string().min(1),
5662
- startedAt: isoTimestamp
5931
+ startedAt: isoTimestamp,
5932
+ childStartedAt: z3.string().min(1).nullable().optional(),
5933
+ childCommand: z3.string().min(1).nullable().optional()
5663
5934
  }).strict();
5664
5935
  var InputRecordSchema = z3.object({
5665
5936
  version: z3.literal(1),
@@ -5692,9 +5963,91 @@ var TransitionRecordSchema = z3.object({
5692
5963
  detail: z3.string().optional(),
5693
5964
  at: isoTimestamp
5694
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();
5695
6048
  // src/lib/diff-evidence.ts
5696
- import { execFileSync as execFileSync5 } from "node:child_process";
5697
- import { join as join4, relative, resolve as resolve2 } from "node:path";
6049
+ import { execFileSync as execFileSync6 } from "node:child_process";
6050
+ import { join as join5, relative, resolve as resolve2 } from "node:path";
5698
6051
 
5699
6052
  // src/lib/config.ts
5700
6053
  import { execFileSync as execFileSync2 } from "node:child_process";
@@ -5876,6 +6229,13 @@ var VinayaConfigSchema = z4.object({
5876
6229
  message: "must be at most 3600000 (1 hour)"
5877
6230
  });
5878
6231
  }
6232
+ }).optional(),
6233
+ logPublish: z4.object({
6234
+ issue: z4.number().int().positive().optional(),
6235
+ pr: z4.number().int().positive().optional(),
6236
+ maxChunksPerFlush: z4.number().int().positive().optional()
6237
+ }).refine((v) => !(v.issue !== undefined && v.pr !== undefined), {
6238
+ message: "logPublish: set at most one of issue/pr, not both"
5879
6239
  }).optional()
5880
6240
  });
5881
6241
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
@@ -6223,6 +6583,16 @@ var COMMANDS = [
6223
6583
  ],
6224
6584
  status: "shipped"
6225
6585
  },
6586
+ {
6587
+ name: "task-tools serve",
6588
+ description: "Run the task-operator MCP tool server over stdio — the command both runtime adapters register",
6589
+ details: [
6590
+ "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.",
6591
+ "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.",
6592
+ "`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."
6593
+ ],
6594
+ status: "shipped"
6595
+ },
6226
6596
  {
6227
6597
  name: "pr create",
6228
6598
  description: "Open a pull request after full brief-schema validation",
@@ -6367,6 +6737,30 @@ var COMMANDS = [
6367
6737
  ],
6368
6738
  status: "shipped"
6369
6739
  },
6740
+ {
6741
+ name: "log export-artifact",
6742
+ description: "Copy this run's own local outbox into a file, for the generated task-path workflow's own upload step",
6743
+ flags: [],
6744
+ details: [
6745
+ "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.",
6746
+ "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."
6747
+ ],
6748
+ status: "shipped"
6749
+ },
6750
+ {
6751
+ name: "log collect-artifact",
6752
+ description: "Validate a downloaded task-log artifact and publish the valid records through the existing flush path",
6753
+ flags: [
6754
+ { flag: "--pr", description: "The pull request this artifact belongs to" },
6755
+ { flag: "--repo", description: "Expected `owner/repo` — a provenance cross-check against each record" }
6756
+ ],
6757
+ details: [
6758
+ "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.",
6759
+ "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.",
6760
+ "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."
6761
+ ],
6762
+ status: "shipped"
6763
+ },
6370
6764
  {
6371
6765
  name: "milestone create",
6372
6766
  description: "Create a GitHub Milestone from a validated body",
@@ -7429,6 +7823,252 @@ function coreCheckRegistry() {
7429
7823
  }
7430
7824
  var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
7431
7825
 
7826
+ // src/lib/log-sink.ts
7827
+ import { execFileSync as execFileSync5 } from "node:child_process";
7828
+ import { randomUUID } from "node:crypto";
7829
+ import {
7830
+ closeSync as closeSync2,
7831
+ constants as fsConstants2,
7832
+ existsSync as existsSync4,
7833
+ fstatSync as fstatSync2,
7834
+ mkdirSync as mkdirSync2,
7835
+ openSync as openSync2,
7836
+ readFileSync as readFileSync3,
7837
+ renameSync,
7838
+ writeSync
7839
+ } from "node:fs";
7840
+ import { hostname as osHostname, homedir as homedir2 } from "node:os";
7841
+ import { dirname as dirname3, join as join4 } from "node:path";
7842
+ var OUTBOX_MAX_BYTES = 8 * 1024 * 1024;
7843
+ function isEnoent(err) {
7844
+ return typeof err === "object" && err !== null && err.code === "ENOENT";
7845
+ }
7846
+ function resolveDoctrine(cwd, vinayaVersion) {
7847
+ let toplevel;
7848
+ try {
7849
+ toplevel = execFileSync5("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
7850
+ encoding: "utf8",
7851
+ stdio: ["ignore", "pipe", "pipe"]
7852
+ }).trim();
7853
+ } catch (err) {
7854
+ return isEnoent(err) ? "unknown" : vinayaVersion;
7855
+ }
7856
+ if (!existsSync4(join4(toplevel, "aeg-root", "roles")))
7857
+ return vinayaVersion;
7858
+ try {
7859
+ const sha = execFileSync5("git", ["-C", toplevel, "rev-parse", "HEAD:aeg-root"], {
7860
+ encoding: "utf8",
7861
+ stdio: ["ignore", "pipe", "pipe"]
7862
+ }).trim();
7863
+ return `aeg-root@${sha}`;
7864
+ } catch {
7865
+ return vinayaVersion;
7866
+ }
7867
+ }
7868
+ function readVinayaVersion() {
7869
+ try {
7870
+ const pkg = JSON.parse(readFileSync3(join4(packageRoot(import.meta.url), "package.json"), "utf8"));
7871
+ return pkg.version ?? "unknown";
7872
+ } catch {
7873
+ return "unknown";
7874
+ }
7875
+ }
7876
+ var SAFE_PATH_SEGMENT = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
7877
+ function isSafeRepoSegment(segment) {
7878
+ return SAFE_PATH_SEGMENT.test(segment) && !segment.includes("..");
7879
+ }
7880
+ function outboxPathFor(deps, repo, issue) {
7881
+ const dirName = repo ? `${repo.owner}-${repo.repo}` : "unresolved";
7882
+ const fileName = `${issue ?? "none"}.ndjson`;
7883
+ return join4(deps.outboxRoot(), dirName, fileName);
7884
+ }
7885
+ function hostFromEnv(env) {
7886
+ if (env.GITHUB_ACTIONS)
7887
+ return "ci";
7888
+ if (env.VINAYA_HOST === "hook")
7889
+ return "hook";
7890
+ if (env.VINAYA_HOST === "loop")
7891
+ return "loop";
7892
+ return "cli";
7893
+ }
7894
+ function defaultDeps() {
7895
+ return {
7896
+ outboxRoot: () => join4(GLOBAL_VINAYA_HOME, "outbox"),
7897
+ home: () => homedir2(),
7898
+ hostname: () => osHostname(),
7899
+ cwd: () => process.cwd(),
7900
+ now: () => new Date,
7901
+ env: () => process.env,
7902
+ resolveRepo: () => resolveRepo(),
7903
+ vinayaVersion: () => readVinayaVersion(),
7904
+ stderr: (message) => {
7905
+ process.stderr.write(message);
7906
+ },
7907
+ inputVersions: () => {
7908
+ return;
7909
+ }
7910
+ };
7911
+ }
7912
+ var APPEND_OPEN_FLAGS = fsConstants2.O_WRONLY | fsConstants2.O_APPEND | fsConstants2.O_CREAT | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK;
7913
+ function guardedAppendOpen(path) {
7914
+ try {
7915
+ return openSync2(path, APPEND_OPEN_FLAGS, 384);
7916
+ } catch {
7917
+ return;
7918
+ }
7919
+ }
7920
+ function reportRotationOverflow(backupPath, warn) {
7921
+ let raw;
7922
+ try {
7923
+ raw = readFileSync3(backupPath, "utf8");
7924
+ } catch {
7925
+ return;
7926
+ }
7927
+ const lines = raw.split(`
7928
+ `).filter((l) => l.length > 0);
7929
+ if (lines.length === 0)
7930
+ return;
7931
+ const droppedIdentities = lines.map((line, i) => {
7932
+ try {
7933
+ return recordIdentity(JSON.parse(line)) ?? `unreadable:${i}`;
7934
+ } catch {
7935
+ return `unreadable:${i}`;
7936
+ }
7937
+ });
7938
+ const diagnostic = { reason: "capacity", dropped: droppedIdentities.length, droppedIdentities };
7939
+ const shown = diagnostic.droppedIdentities.slice(0, 20);
7940
+ const more = diagnostic.dropped > shown.length ? `, +${diagnostic.dropped - shown.length} more` : "";
7941
+ warn(`vinaya: log outbox rotation is overwriting ${backupPath} — ${diagnostic.dropped} record(s) permanently lost: ${shown.join(", ")}${more}
7942
+ `);
7943
+ }
7944
+ function appendLine(path, line, warn) {
7945
+ try {
7946
+ mkdirSync2(dirname3(path), { recursive: true, mode: 448 });
7947
+ let fd = guardedAppendOpen(path);
7948
+ if (fd === undefined) {
7949
+ warn(`vinaya: log outbox target could not be opened (symlink, FIFO, or unwritable) — refusing: ${path}
7950
+ `);
7951
+ return;
7952
+ }
7953
+ let closed = false;
7954
+ try {
7955
+ const stat = fstatSync2(fd);
7956
+ if (!stat.isFile()) {
7957
+ warn(`vinaya: log outbox target is not a regular file — refusing to write: ${path}
7958
+ `);
7959
+ return;
7960
+ }
7961
+ if (stat.size > OUTBOX_MAX_BYTES) {
7962
+ closeSync2(fd);
7963
+ closed = true;
7964
+ const backupPath = path.replace(/\.ndjson$/, ".1.ndjson");
7965
+ reportRotationOverflow(backupPath, warn);
7966
+ renameSync(path, backupPath);
7967
+ const rotated = guardedAppendOpen(path);
7968
+ if (rotated === undefined) {
7969
+ warn(`vinaya: log outbox target could not be reopened after rotation — refusing: ${path}
7970
+ `);
7971
+ return;
7972
+ }
7973
+ fd = rotated;
7974
+ closed = false;
7975
+ }
7976
+ writeSync(fd, line);
7977
+ } finally {
7978
+ if (!closed)
7979
+ closeSync2(fd);
7980
+ }
7981
+ } catch (err) {
7982
+ warn(`vinaya: log outbox write failed — ${err instanceof Error ? err.message : String(err)}
7983
+ `);
7984
+ }
7985
+ }
7986
+ function createLogSink(overrides = {}) {
7987
+ const deps = { ...defaultDeps(), ...overrides };
7988
+ const runId = deps.env().VINAYA_RUN_ID || randomUUID();
7989
+ const processId = randomUUID();
7990
+ let seq = 0;
7991
+ let warned = false;
7992
+ const warnOnce = (message) => {
7993
+ if (warned)
7994
+ return;
7995
+ warned = true;
7996
+ try {
7997
+ deps.stderr(message);
7998
+ } catch {}
7999
+ };
8000
+ let doctrineCache;
8001
+ const doctrine = () => {
8002
+ if (doctrineCache === undefined)
8003
+ doctrineCache = resolveDoctrine(deps.cwd(), deps.vinayaVersion());
8004
+ return doctrineCache;
8005
+ };
8006
+ let resolveRepoCache;
8007
+ const resolveRepoOnce = () => {
8008
+ if (resolveRepoCache === undefined)
8009
+ resolveRepoCache = deps.resolveRepo();
8010
+ return resolveRepoCache;
8011
+ };
8012
+ function log(e) {
8013
+ try {
8014
+ const env = deps.env();
8015
+ const host = hostFromEnv(env);
8016
+ const now = deps.now();
8017
+ const mySeq = seq++;
8018
+ resolveRepoOnce().then((resolved) => {
8019
+ const repo = resolved && isSafeRepoSegment(resolved.owner) && isSafeRepoSegment(resolved.repo) ? resolved : null;
8020
+ const header = buildHeader({
8021
+ now,
8022
+ runId,
8023
+ seq: mySeq,
8024
+ repo: repo ? `${repo.owner}/${repo.repo}` : null,
8025
+ vinaya: deps.vinayaVersion(),
8026
+ doctrine: doctrine(),
8027
+ host,
8028
+ hostname: deps.hostname(),
8029
+ env: {
8030
+ role: env.VINAYA_ROLE,
8031
+ task: env.VINAYA_TASK,
8032
+ round: env.VINAYA_ROUND,
8033
+ run: env.VINAYA_RUN,
8034
+ attempt: env.VINAYA_ATTEMPT,
8035
+ parent: env.VINAYA_PARENT_EVENT
8036
+ },
8037
+ eventId: randomUUID(),
8038
+ processId,
8039
+ inputVersions: deps.inputVersions()
8040
+ });
8041
+ const full = { ...e, ...header };
8042
+ const parsed = LogEventSchema.safeParse(full);
8043
+ if (!parsed.success) {
8044
+ warnOnce(`vinaya: log() refused an invalid payload — ${parsed.error.issues[0]?.message ?? "schema violation"}
8045
+ `);
8046
+ return;
8047
+ }
8048
+ const line = `${JSON.stringify(redact(parsed.data, deps.home()))}
8049
+ `;
8050
+ appendLine(outboxPathFor(deps, repo, header.subject.issue), line, warnOnce);
8051
+ }).catch((err) => {
8052
+ warnOnce(`vinaya: log() failed — ${err instanceof Error ? err.message : String(err)}
8053
+ `);
8054
+ });
8055
+ } catch (err) {
8056
+ warnOnce(`vinaya: log() failed — ${err instanceof Error ? err.message : String(err)}
8057
+ `);
8058
+ }
8059
+ }
8060
+ const warmup = () => {
8061
+ doctrine();
8062
+ const originalWarn = console.warn;
8063
+ console.warn = () => {};
8064
+ resolveRepoOnce().finally(() => {
8065
+ console.warn = originalWarn;
8066
+ });
8067
+ };
8068
+ return { log, runId, warmup };
8069
+ }
8070
+ var defaultSink = createLogSink();
8071
+
7432
8072
  // src/checks/contract.ts
7433
8073
  var CHECK_SCHEMA_VERSION = 1;
7434
8074
  function emitCheckError(error) {
@@ -7437,7 +8077,7 @@ function emitCheckError(error) {
7437
8077
  }
7438
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
  }
@@ -7568,7 +8208,7 @@ function findingsInThisDiff(findings) {
7568
8208
  var CHECK_NAME = "doc-coverage-push";
7569
8209
  function git2(args) {
7570
8210
  try {
7571
- return execFileSync6("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
8211
+ return execFileSync7("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
7572
8212
  } catch {
7573
8213
  return "";
7574
8214
  }
@@ -7582,7 +8222,7 @@ function resolvePrBody() {
7582
8222
  return process.env.PR_BODY;
7583
8223
  if (process.env.PR_BODY_FILE) {
7584
8224
  try {
7585
- return readFileSync3(process.env.PR_BODY_FILE, "utf8");
8225
+ return readFileSync4(process.env.PR_BODY_FILE, "utf8");
7586
8226
  } catch {
7587
8227
  return "";
7588
8228
  }
@@ -7617,8 +8257,8 @@ function main() {
7617
8257
  }
7618
8258
  if (changed.length === 0)
7619
8259
  process.exit(0);
7620
- const content = existsSync4(DOC_OWNERS_PATH) ? readFileSync3(DOC_OWNERS_PATH, "utf8") : null;
7621
- const result = evaluateC5(changed, content, resolvePrBody(), existsSync4, waiverActiveFromEnv(), (p) => fileDiffAgainst(ref, p));
8260
+ const content = existsSync5(DOC_OWNERS_PATH) ? readFileSync4(DOC_OWNERS_PATH, "utf8") : null;
8261
+ const result = evaluateC5(changed, content, resolvePrBody(), existsSync5, waiverActiveFromEnv(), (p) => fileDiffAgainst(ref, p));
7622
8262
  for (const message of result.errors) {
7623
8263
  emitCheckError({
7624
8264
  schema: CHECK_SCHEMA_VERSION,