@attalabs/vinaya 0.21.0 → 0.23.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 (211) hide show
  1. package/README.md +15 -4
  2. package/aeg-root/contracts/archivist-tranche-archivist.md +2 -2
  3. package/aeg-root/contracts/brief-developer.md +1 -0
  4. package/aeg-root/contracts/developer-reviewer.md +3 -3
  5. package/aeg-root/contracts/reviewer-archivist.md +3 -3
  6. package/aeg-root/enforcement.md +28 -21
  7. package/aeg-root/process.md +8 -4
  8. package/aeg-root/roles/archivist.md +2 -2
  9. package/aeg-root/roles/brief-author.md +9 -3
  10. package/aeg-root/roles/developer.md +63 -31
  11. package/aeg-root/roles/planner.md +2 -2
  12. package/aeg-root/roles/principal.md +14 -0
  13. package/aeg-root/roles/reviewer.md +32 -15
  14. package/aeg-root/roles/security.md +15 -5
  15. package/aeg-root/skills/aeg/SKILL.md +9 -5
  16. package/aeg-root/skills/aeg-roles/SKILL.md +2 -2
  17. package/aeg-root/skills/brief-authoring/SKILL.md +39 -27
  18. package/aeg-root/state-machine.md +9 -11
  19. package/aeg-root/templates/brief-template.md +6 -6
  20. package/aeg-root/templates/pr-report-template.md +12 -7
  21. package/aeg-root/tranche-model.md +9 -5
  22. package/dist/checks/bin/check-body-bare-digits.js +667 -148
  23. package/dist/checks/bin/check-branch-topology.js +714 -149
  24. package/dist/checks/bin/check-brief-shape.js +704 -147
  25. package/dist/checks/bin/check-changeset-coverage.js +1046 -166
  26. package/dist/checks/bin/check-closes-n.js +714 -149
  27. package/dist/checks/bin/check-coherence.js +729 -156
  28. package/dist/checks/bin/check-dead-branch-push.js +663 -144
  29. package/dist/checks/bin/check-dispatch-readiness.js +728 -155
  30. package/dist/checks/bin/check-doc-coverage-push.js +838 -261
  31. package/dist/checks/bin/check-doc-coverage.js +838 -261
  32. package/dist/checks/bin/check-doctrine-no-procedures.js +4794 -0
  33. package/dist/checks/bin/check-doctrine-portability.js +799 -155
  34. package/dist/checks/bin/check-evidence-fresh.js +1132 -206
  35. package/dist/checks/bin/check-exec-bits.js +4931 -0
  36. package/dist/checks/bin/check-first-push-dispatch.js +716 -151
  37. package/dist/checks/bin/check-issue-assignment.js +714 -149
  38. package/dist/checks/bin/check-main-branch-refusal.js +663 -144
  39. package/dist/checks/bin/check-no-disk-state.js +663 -144
  40. package/dist/checks/bin/check-pr-report-density.js +4461 -0
  41. package/dist/checks/bin/check-quoted-command.js +741 -166
  42. package/dist/checks/bin/check-reader-resolvable-prose.js +768 -173
  43. package/dist/checks/bin/check-registry-gates.js +726 -154
  44. package/dist/checks/bin/check-retired-vocabulary.js +760 -165
  45. package/dist/checks/bin/check-review-gate.js +783 -148
  46. package/dist/checks/bin/check-single-plan-pr.js +663 -144
  47. package/dist/checks/bin/check-test-plan.js +664 -145
  48. package/dist/checks/bin/check-token-collection-wired.js +665 -146
  49. package/dist/checks/bin/check-token-report.js +4509 -0
  50. package/dist/checks/bin/check-workspace-escape.js +1129 -183
  51. package/dist/index.js +8458 -5403
  52. package/package.json +1 -1
  53. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/actions.ts +173 -0
  54. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/anchored-region.ts +113 -0
  55. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/archive-task.ts +206 -0
  56. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/baseline-capture.ts +65 -0
  57. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/blast-radius-domains.ts +192 -0
  58. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/branch-topology-gate.ts +85 -0
  59. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-validation.ts +511 -0
  60. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/claude-code-transcript.ts +460 -0
  61. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/coherence-checks.ts +787 -0
  62. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dead-branch-push-audit.ts +55 -0
  63. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dead-branch-push-guard.ts +77 -0
  64. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/derive-section7.ts +66 -0
  65. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/derive-tranche.ts +94 -0
  66. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/diagram-model.ts +372 -0
  67. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/direct-main-push.ts +39 -0
  68. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dispatch-gate.ts +224 -0
  69. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doc-owners.ts +314 -0
  70. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/build-doc-nav.ts +38 -0
  71. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/docs-coherence.ts +136 -0
  72. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/index.ts +29 -0
  73. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/legacy-anchors.ts +48 -0
  74. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/nav-helpers.ts +17 -0
  75. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/node-route.ts +134 -0
  76. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/parse-doc.ts +43 -0
  77. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/published-prose.ts +306 -0
  78. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/surfaced-manifest.ts +89 -0
  79. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/docs/types.ts +33 -0
  80. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-portability.ts +170 -0
  81. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-source.ts +23 -0
  82. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/ensure-label.ts +28 -0
  83. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/file-classify.ts +51 -0
  84. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/first-push-dispatch-gate.ts +80 -0
  85. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/gate-audience.ts +168 -0
  86. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/index.ts +301 -0
  87. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/issue-assignment.ts +102 -0
  88. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/issue-validation.ts +746 -0
  89. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/leftover-detection.ts +68 -0
  90. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/local-anchor-coverage.ts +245 -0
  91. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/main-branch-refusal.ts +64 -0
  92. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/manifest-validity.ts +81 -0
  93. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/markdown-table.ts +76 -0
  94. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/metering-io-guard.ts +119 -0
  95. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/milestone-validation.ts +240 -0
  96. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/no-disk-state.ts +50 -0
  97. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-ledger.ts +77 -0
  98. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-registry.ts +105 -0
  99. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-token-report.ts +165 -0
  100. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-tranche.ts +183 -0
  101. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/pr-tier.ts +81 -0
  102. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/premise-check.ts +125 -0
  103. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/quoted-command.ts +275 -0
  104. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/reader-resolvable-prose.ts +320 -0
  105. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/registry-checks.ts +255 -0
  106. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/registry-parse.ts +164 -0
  107. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/registry-scaffold.ts +216 -0
  108. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/report-tokens.ts +261 -0
  109. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/retired-vocabulary.ts +163 -0
  110. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-gate.ts +269 -0
  111. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/single-plan-pr.ts +73 -0
  112. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/state-machine-model.ts +269 -0
  113. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/state-source.ts +14 -0
  114. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/status-block.ts +8 -0
  115. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/sum-ledger.ts +26 -0
  116. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/symbol-collisions.ts +78 -0
  117. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-gate.ts +104 -0
  118. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-section.ts +65 -0
  119. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/types.ts +174 -0
  120. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/verdict-extraction.ts +140 -0
  121. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/vocabulary-citation.ts +114 -0
  122. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/waiver-label.ts +52 -0
  123. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/workspace-escape.ts +172 -0
  124. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  125. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  126. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  127. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  128. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  129. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  130. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  131. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  132. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  133. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  134. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  135. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  136. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  137. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  138. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  139. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  140. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  141. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  142. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  143. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  144. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  145. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  146. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  147. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  148. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  149. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  150. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  151. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  152. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  153. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  154. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  155. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  156. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  157. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  158. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  159. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  160. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  161. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  162. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
  163. 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
  164. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
  165. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
  166. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__02c71kv._.js +1 -1
  167. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
  168. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
  169. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
  170. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0ozbaq0._.js → [root-of-the-server]__1mcitst._.js} +1 -1
  171. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  172. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
  173. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
  174. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
  175. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0o54suy._.js +1 -1
  176. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1-6rzip._.js +1 -1
  177. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +1 -1
  178. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1mrtj0f._.js +1 -1
  179. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
  180. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_app_studio_06it6s-._.js +1 -1
  181. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  182. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/packages_ui_libraries_animate_components_index_ts_186fje1._.js +1 -1
  183. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/packages_ui_libraries_brutal_components_index_ts_0n_lcc4._.js +1 -1
  184. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/packages_ui_libraries_retro_components_index_ts_0wvpm_z._.js +1 -1
  185. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/packages_ui_libraries_retro_components_index_ts_1_cfi48._.js +1 -1
  186. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  187. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  188. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  189. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  190. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/00pi8vhg8w6gi.js +1 -0
  191. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0r4i_ks9z02bw.js → 03q0q9n6-xah8.js} +1 -1
  192. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2_k6j1o9q5nn4.js → 0jer_drkl1e4r.js} +4 -4
  193. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/0vc2u252q5q6o.js +1 -0
  194. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1k916ih3p4nsg.js → 13g16cu00g570.js} +1 -1
  195. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2hmannd4bjhzy.js → 1sfxfunirkcr6.js} +1 -1
  196. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1t2kft7bxzuxn.js → 206ajg8f1a-_h.js} +1 -1
  197. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1hfhxjy577l5d.js → 20x-t0a0v129z.js} +1 -1
  198. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{25kbt5k-xb2a5.js → 279qq4twinc97.js} +1 -1
  199. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{112g08xpx3kro.js → 2ka8dg687auhj.js} +1 -1
  200. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/2ou_81ahqw31e.js +1 -0
  201. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0up5x3jg-cwov.js → 2rn89uwtf2lwc.js} +1 -1
  202. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3mqu_uhluy19i.css → 3qc67qhcvbg0o.css} +1 -1
  203. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1yqs2zgvwa4p1.js → 3zugx9esh2x51.js} +1 -1
  204. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  205. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/0_jnwbarv-cqk.js +0 -1
  206. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/1ybrihl5_aocy.js +0 -1
  207. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/3r5efmleneaco.js +0 -1
  208. /package/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/{aeg-core → vinaya/studio-standalone/_node_modules/@attalabs/aeg-core}/bin/verify-coherence.ts +0 -0
  209. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{crNCHQ9uAgKOYC6DKkZma → 5xy_GA7G5KRuE3HDumIiw}/_buildManifest.js +0 -0
  210. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{crNCHQ9uAgKOYC6DKkZma → 5xy_GA7G5KRuE3HDumIiw}/_clientMiddlewareManifest.js +0 -0
  211. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{crNCHQ9uAgKOYC6DKkZma → 5xy_GA7G5KRuE3HDumIiw}/_ssgManifest.js +0 -0
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
2
4
  // ../../packages/aeg-core/src/gate-audience.ts
3
5
  var GATE_AUDIENCE = {
4
6
  "check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
@@ -207,17 +209,18 @@ function splitIds(raw) {
207
209
  return [];
208
210
  return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0 && !isEmptyMarker(s) && ID_TOKEN.test(s));
209
211
  }
210
- function resolveIds(raw, lastSlug) {
212
+ function resolveIds(raw) {
211
213
  const ids = splitIds(raw);
212
214
  const resolved = [];
215
+ let lastSlug = null;
213
216
  for (const id of ids) {
214
217
  const qualified = id.match(SLUG_QUALIFIED_ID);
215
218
  if (qualified) {
216
- lastSlug.current = qualified[1] ?? null;
219
+ lastSlug = qualified[1] ?? null;
217
220
  resolved.push(id);
218
- } else if (lastSlug.current) {
219
- resolved.push(`${lastSlug.current} ${id}`);
220
- lastSlug.current = null;
221
+ } else if (lastSlug) {
222
+ resolved.push(`${lastSlug} ${id}`);
223
+ lastSlug = null;
221
224
  } else {
222
225
  resolved.push(id);
223
226
  }
@@ -237,12 +240,7 @@ function parseRationaleDeps(body) {
237
240
  const section = extractSection(body);
238
241
  const dependsOn = [];
239
242
  const conflictsWith = [];
240
- let current = null;
241
243
  const assignedFields = new Set;
242
- const lastSlugByField = {
243
- dependsOn: { current: null },
244
- conflictsWith: { current: null }
245
- };
246
244
  const spanPattern = /`([^`]*)`/g;
247
245
  let match = spanPattern.exec(section);
248
246
  while (match !== null) {
@@ -252,13 +250,9 @@ function parseRationaleDeps(body) {
252
250
  const field = labelMatch[1]?.toLowerCase() === "conflicts-with" ? "conflictsWith" : "dependsOn";
253
251
  if (!assignedFields.has(field)) {
254
252
  assignedFields.add(field);
255
- current = field;
256
- const ids = resolveIds(labelMatch[2] ?? "", lastSlugByField[field]);
253
+ const ids = resolveIds(labelMatch[2] ?? "");
257
254
  pushUnique(field === "dependsOn" ? dependsOn : conflictsWith, ids);
258
255
  }
259
- } else if (current) {
260
- const ids = resolveIds(content, lastSlugByField[current]);
261
- pushUnique(current === "dependsOn" ? dependsOn : conflictsWith, ids);
262
256
  }
263
257
  match = spanPattern.exec(section);
264
258
  }
@@ -1723,6 +1717,48 @@ function splitTableRow(row) {
1723
1717
  parts.pop();
1724
1718
  return parts.map((p) => p.split(PLACEHOLDER).join("|"));
1725
1719
  }
1720
+ // ../../packages/aeg-core/src/blast-radius-domains.ts
1721
+ function resolveWorkspaceEntry(entry2, listDirs) {
1722
+ if (entry2.endsWith("/*")) {
1723
+ const baseDir = entry2.slice(0, -2);
1724
+ return listDirs(baseDir).map((name) => `${baseDir}/${name}`);
1725
+ }
1726
+ if (entry2.includes("*"))
1727
+ return [];
1728
+ return [entry2];
1729
+ }
1730
+ // ../../packages/aeg-core/src/consumer-enumeration.ts
1731
+ function deriveWorkspaceMemberDirs(workspaces, listDirs) {
1732
+ const positive = workspaces.filter((entry2) => !entry2.startsWith("!"));
1733
+ const negative = workspaces.filter((entry2) => entry2.startsWith("!")).map((entry2) => entry2.slice(1));
1734
+ const excluded = new Set;
1735
+ for (const entry2 of negative) {
1736
+ for (const member of resolveWorkspaceEntry(entry2, listDirs))
1737
+ excluded.add(member);
1738
+ }
1739
+ const members = new Set;
1740
+ for (const entry2 of positive) {
1741
+ for (const member of resolveWorkspaceEntry(entry2, listDirs)) {
1742
+ if (!excluded.has(member))
1743
+ members.add(member);
1744
+ }
1745
+ }
1746
+ return [...members].sort();
1747
+ }
1748
+ function buildConsumersOf(workspaces, listDirs, readManifest) {
1749
+ const members = deriveWorkspaceMemberDirs(workspaces, listDirs);
1750
+ return (pkg) => {
1751
+ const target = `@attalabs/${pkg}`;
1752
+ return members.filter((dir) => {
1753
+ if (dir === `packages/${pkg}`)
1754
+ return false;
1755
+ const manifest = readManifest(dir);
1756
+ if (!manifest)
1757
+ return false;
1758
+ return target in (manifest.dependencies ?? {}) || target in (manifest.devDependencies ?? {});
1759
+ });
1760
+ };
1761
+ }
1726
1762
  // ../../packages/aeg-core/src/state-machine-model.ts
1727
1763
  var FORGE_FACT_INPUTS = [
1728
1764
  {
@@ -1788,6 +1824,156 @@ var DERIVED_STATUSES = [
1788
1824
  "incoherent"
1789
1825
  ];
1790
1826
  var DERIVABLE_STATUSES = DERIVED_STATUSES.filter((s) => s !== "backlog");
1827
+ // ../../packages/aeg-core/src/parse-ledger.ts
1828
+ function rowFromCells(cells) {
1829
+ if (cells.length < 7)
1830
+ return null;
1831
+ const phase = (cells[0] ?? "").trim();
1832
+ if (!phase)
1833
+ return null;
1834
+ return {
1835
+ phase,
1836
+ role: (cells[1] ?? "").trim(),
1837
+ agentModel: (cells[2] ?? "").trim(),
1838
+ tokensIn: parseIntCell(cells[3] ?? ""),
1839
+ tokensOut: parseIntCell(cells[4] ?? ""),
1840
+ cost: parseCostCell(cells[5] ?? ""),
1841
+ date: (cells[6] ?? "").trim()
1842
+ };
1843
+ }
1844
+ function parseIntCell(cell) {
1845
+ const trimmed = cell.replace(/`/g, "").trim();
1846
+ if (!trimmed || isEmDashOrDash(trimmed))
1847
+ return null;
1848
+ const normalized = trimmed.replace(/[,_\s]/g, "");
1849
+ if (!/^-?\d+$/.test(normalized))
1850
+ return null;
1851
+ return Number(normalized);
1852
+ }
1853
+ function parseCostCell(cell) {
1854
+ const trimmed = cell.replace(/`/g, "").trim();
1855
+ if (!trimmed || isEmDashOrDash(trimmed))
1856
+ return null;
1857
+ const normalized = trimmed.replace(/^\$/, "").replace(/[,_\s]/g, "");
1858
+ if (!/^-?\d+(\.\d+)?$/.test(normalized))
1859
+ return null;
1860
+ return Number(normalized);
1861
+ }
1862
+ function isEmDashOrDash(s) {
1863
+ const t = s.trim();
1864
+ return t === "—" || t === "-" || t === "–";
1865
+ }
1866
+ // ../../packages/aeg-core/src/waiver-label.ts
1867
+ var WAIVER_LABEL = label("waiver-docs");
1868
+ var WAIVER_LABEL_REVIEW = label("waiver-review");
1869
+ var PRINCIPAL_ALLOWLIST = ["daniboomerang"];
1870
+ function isPrincipal(login, principalAllowlist) {
1871
+ if (login === null)
1872
+ return false;
1873
+ const normalized = login.toLowerCase();
1874
+ return principalAllowlist.some((p) => p.toLowerCase() === normalized);
1875
+ }
1876
+ function isWaiverLabelActorVerified(opts) {
1877
+ if (!opts.labels.includes(opts.label))
1878
+ return false;
1879
+ return isPrincipal(opts.labelActor, opts.principalAllowlist);
1880
+ }
1881
+
1882
+ // ../../packages/aeg-core/src/parse-token-report.ts
1883
+ var TOKEN_REPORT_HEADING = /^\s*(?:#{1,6}\s*Token report\s*|\*\*Token report\*\*)\s*$/i;
1884
+ var INLINE_FIELD_LIST = /Phase:\s*([^|]+?)\s*\|\s*Role:\s*([^|]+?)\s*\|\s*Agent\/Model:\s*([^|]+?)\s*\|\s*Tokens in:\s*([^|]+?)\s*\|\s*Tokens out:\s*([^|]+?)\s*\|\s*Cost:\s*([^|]+?)\s*\|\s*Date:\s*(.+?)\s*$/im;
1885
+ function parseTokenReportEntries(body) {
1886
+ const lines = body.split(/\r?\n/);
1887
+ const headingIdxs = [];
1888
+ lines.forEach((line, i) => {
1889
+ if (TOKEN_REPORT_HEADING.test(line))
1890
+ headingIdxs.push(i);
1891
+ });
1892
+ const out = [];
1893
+ for (let h = 0;h < headingIdxs.length; h++) {
1894
+ const start = headingIdxs[h] + 1;
1895
+ const end = h + 1 < headingIdxs.length ? headingIdxs[h + 1] : lines.length;
1896
+ const section = lines.slice(start, end).join(`
1897
+ `);
1898
+ const tableRows = parseTableSection(section);
1899
+ if (tableRows.length > 0) {
1900
+ out.push(...tableRows);
1901
+ continue;
1902
+ }
1903
+ const inlineRow = parseInlineFieldList(section);
1904
+ if (inlineRow)
1905
+ out.push(inlineRow);
1906
+ }
1907
+ return out;
1908
+ }
1909
+ function parseTableSection(section) {
1910
+ const lines = section.split(/\r?\n/);
1911
+ let i = 0;
1912
+ for (;i < lines.length; i++) {
1913
+ if ((lines[i] ?? "").trim().startsWith("|"))
1914
+ break;
1915
+ }
1916
+ if (i + 1 >= lines.length)
1917
+ return [];
1918
+ i += 2;
1919
+ const out = [];
1920
+ for (;i < lines.length; i++) {
1921
+ const trimmed = (lines[i] ?? "").trim();
1922
+ if (!trimmed.startsWith("|"))
1923
+ break;
1924
+ const row = rowFromCells(splitTableRow(trimmed));
1925
+ if (row)
1926
+ out.push(row);
1927
+ }
1928
+ return out;
1929
+ }
1930
+ function parseInlineFieldList(section) {
1931
+ const m = section.match(INLINE_FIELD_LIST);
1932
+ if (!m)
1933
+ return null;
1934
+ return rowFromCells([m[1], m[2], m[3], m[4], m[5], m[6], m[7]]);
1935
+ }
1936
+ // ../../packages/aeg-core/src/verdict-extraction.ts
1937
+ var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
1938
+ function firstThreeLines(comment) {
1939
+ return comment.split(`
1940
+ `).slice(0, 3).join(`
1941
+ `);
1942
+ }
1943
+ function extractHeadSha(comment) {
1944
+ const m = firstThreeLines(comment).match(HEAD_SHA_PATTERN);
1945
+ return m ? m[1].toLowerCase() : null;
1946
+ }
1947
+ function extractVerdict(comments, valuePattern, missingLabel) {
1948
+ const candidates = comments.filter((c) => valuePattern.test(c));
1949
+ if (candidates.length === 0) {
1950
+ return {
1951
+ value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
1952
+ headSha: null,
1953
+ danglingNote: `no ${missingLabel} verdict comment found on this PR`
1954
+ };
1955
+ }
1956
+ const latest = candidates[candidates.length - 1];
1957
+ const m = firstThreeLines(latest).match(valuePattern);
1958
+ if (!m) {
1959
+ return {
1960
+ value: `the most recent ${missingLabel} comment's VERDICT line is not within its first three lines — DANGLING, see below`,
1961
+ headSha: null,
1962
+ danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-three-line read window`
1963
+ };
1964
+ }
1965
+ return {
1966
+ value: m[1].toUpperCase().replace(/[_-]/g, " "),
1967
+ headSha: extractHeadSha(latest),
1968
+ danglingNote: null
1969
+ };
1970
+ }
1971
+ function extractCodeReviewVerdict(comments) {
1972
+ return extractVerdict(comments, /^[ \t]*(?:\*{1,3}|_{1,3})?VERDICT:\s*(APPROVE|REQUEST[ _-]?CHANGES|LGTM)(?![A-Za-z0-9])/im, "code-reviewer");
1973
+ }
1974
+ function extractSecurityReviewVerdict(comments) {
1975
+ return extractVerdict(comments, /^[ \t]*(?:\*{1,3}|_{1,3})?VERDICT:\s*(PASS|FAIL)(?![A-Za-z0-9])/im, "security-review");
1976
+ }
1791
1977
  // ../../packages/aeg-core/src/report-tokens.ts
1792
1978
  var DASH_LOOKALIKES = {
1793
1979
  "-": "‑",
@@ -1796,6 +1982,7 @@ var DASH_LOOKALIKES = {
1796
1982
  };
1797
1983
  var DASH_CHARS = new Set(Object.keys(DASH_LOOKALIKES));
1798
1984
  // ../../packages/aeg-core/src/claude-code-transcript.ts
1985
+ import { createHash } from "node:crypto";
1799
1986
  function summarizeTranscript(jsonl) {
1800
1987
  const seen = new Set;
1801
1988
  const components = {
@@ -1844,7 +2031,14 @@ function numberOr(value, fallback) {
1844
2031
  function sanitizeKey(value) {
1845
2032
  return value.replace(/[^A-Za-z0-9]+/g, "-");
1846
2033
  }
2034
+ function collisionResistantKey(value) {
2035
+ const digest = createHash("sha256").update(value).digest("hex");
2036
+ return `${sanitizeKey(value)}-${digest}`;
2037
+ }
1847
2038
  function transcriptPointerPath(projectDir, tmpDir) {
2039
+ return `${tmpDir}/claude-transcript-${collisionResistantKey(projectDir)}.txt`;
2040
+ }
2041
+ function legacyTranscriptPointerPath(projectDir, tmpDir) {
1848
2042
  return `${tmpDir}/claude-transcript-${sanitizeKey(projectDir)}.txt`;
1849
2043
  }
1850
2044
  function resolvePointer(explicitTranscriptPath, deps) {
@@ -1852,13 +2046,15 @@ function resolvePointer(explicitTranscriptPath, deps) {
1852
2046
  return { path: explicitTranscriptPath, corroborated: true };
1853
2047
  const projectDir = deps.env.CLAUDE_PROJECT_DIR ?? deps.cwd;
1854
2048
  const tmpDir = deps.env.TMPDIR ?? "/tmp";
1855
- const pointerPath = transcriptPointerPath(projectDir, tmpDir);
2049
+ const primaryPointerPath = transcriptPointerPath(projectDir, tmpDir);
2050
+ const legacyPointerPath = legacyTranscriptPointerPath(projectDir, tmpDir);
1856
2051
  const currentSessionId = deps.env.CLAUDE_CODE_SESSION_ID;
1857
- if (!deps.exists(pointerPath)) {
2052
+ const pointerPath = deps.exists(primaryPointerPath) ? primaryPointerPath : deps.exists(legacyPointerPath) ? legacyPointerPath : undefined;
2053
+ if (!pointerPath) {
1858
2054
  return {
1859
2055
  pointerExisted: false,
1860
2056
  corroborated: false,
1861
- error: `No transcript pointer at ${pointerPath} and no --transcript given. ` + "Either this repo installs no track-transcript.sh Stop hook (lacking one is not a defect — " + "name the transcript directly instead), or no session has completed a turn yet."
2057
+ error: `No transcript pointer at ${primaryPointerPath} (nor its pre-migration name ${legacyPointerPath}) ` + "and no --transcript given. Either this repo installs no track-transcript.sh Stop hook (lacking one " + "is not a defect — name the transcript directly instead), or no session has completed a turn yet."
1862
2058
  };
1863
2059
  }
1864
2060
  let contents;
@@ -1933,28 +2129,15 @@ function isTokenCollectionWiringBroken(capability) {
1933
2129
  return false;
1934
2130
  return capability.reason !== "no-transcript-resolved";
1935
2131
  }
2132
+ // ../../packages/aeg-core/src/metering-io-guard.ts
2133
+ import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync } from "node:fs";
2134
+ var GUARD_OPEN_FLAGS = fsConstants.O_RDONLY | fsConstants.O_NONBLOCK | fsConstants.O_NOFOLLOW;
1936
2135
  // ../../packages/aeg-core/src/file-classify.ts
1937
2136
  function isCodeFile(p) {
1938
2137
  if (p.endsWith(".md"))
1939
2138
  return false;
1940
2139
  return /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|sql|css)$/.test(p);
1941
2140
  }
1942
- // ../../packages/aeg-core/src/waiver-label.ts
1943
- var WAIVER_LABEL = label("waiver-docs");
1944
- var WAIVER_LABEL_REVIEW = label("waiver-review");
1945
- var PRINCIPAL_ALLOWLIST = ["daniboomerang"];
1946
- function isPrincipal(login, principalAllowlist) {
1947
- if (login === null)
1948
- return false;
1949
- const normalized = login.toLowerCase();
1950
- return principalAllowlist.some((p) => p.toLowerCase() === normalized);
1951
- }
1952
- function isWaiverLabelActorVerified(opts) {
1953
- if (!opts.labels.includes(opts.label))
1954
- return false;
1955
- return isPrincipal(opts.labelActor, opts.principalAllowlist);
1956
- }
1957
-
1958
2141
  // ../../packages/aeg-core/src/doc-owners.ts
1959
2142
  var DOC_OWNERS_PATH = ".vinaya/doc-owners";
1960
2143
  function parseDocOwners(content) {
@@ -2139,7 +2322,7 @@ function overrideActive(opts) {
2139
2322
  return false;
2140
2323
  }
2141
2324
  // ../../packages/aeg-core/src/premise-check.ts
2142
- import { createHash } from "node:crypto";
2325
+ import { createHash as createHash2 } from "node:crypto";
2143
2326
  var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
2144
2327
  function isPremiseHeader(line) {
2145
2328
  const stripped = line.replace(/[*#]/g, "").trim();
@@ -2171,6 +2354,32 @@ function parsePremiseBlock(prBody) {
2171
2354
  }
2172
2355
  return assertions;
2173
2356
  }
2357
+ function premiseBlockText(prBody) {
2358
+ const searchIn = anchoredRegion(prBody, "PREMISE") ?? prBody;
2359
+ const lines = searchIn.split(/\r?\n/);
2360
+ let startIdx = -1;
2361
+ let endIdxExclusive = lines.length;
2362
+ for (let i = 0;i < lines.length; i++) {
2363
+ const line = lines[i].trim();
2364
+ if (startIdx === -1) {
2365
+ if (isPremiseHeader(line))
2366
+ startIdx = i;
2367
+ continue;
2368
+ }
2369
+ if (line === "") {
2370
+ endIdxExclusive = i;
2371
+ break;
2372
+ }
2373
+ if (!PREMISE_LINE.test(line)) {
2374
+ endIdxExclusive = i;
2375
+ break;
2376
+ }
2377
+ }
2378
+ if (startIdx === -1)
2379
+ return null;
2380
+ return lines.slice(startIdx, endIdxExclusive).join(`
2381
+ `);
2382
+ }
2174
2383
  function checkPremises(assertions, fileReader) {
2175
2384
  const failures = [];
2176
2385
  for (const a of assertions) {
@@ -2184,7 +2393,7 @@ function checkPremises(assertions, fileReader) {
2184
2393
  } else if (a.kind === "absent" && content.includes(a.value)) {
2185
2394
  failures.push(`premise-check: ${a.path} now contains "${a.value}", but the brief pinned it absent — the premise has moved; re-dig before proceeding.`);
2186
2395
  } else if (a.kind === "sha256") {
2187
- const actual = createHash("sha256").update(content).digest("hex");
2396
+ const actual = createHash2("sha256").update(content).digest("hex");
2188
2397
  if (actual !== a.value.toLowerCase()) {
2189
2398
  failures.push(`premise-check: ${a.path} sha256 mismatch (brief pinned ${a.value}, file is now ${actual}) — the file changed since this brief was authored; re-dig before proceeding.`);
2190
2399
  }
@@ -2257,15 +2466,18 @@ function testPlanRegion(prBody) {
2257
2466
  return located.found ? located.section : prBody;
2258
2467
  }
2259
2468
  function checkTestPlan(prBody) {
2260
- const region = testPlanRegion(prBody);
2469
+ const located = locateTestPlanSection(prBody);
2470
+ const region = located.found ? located.section : prBody;
2261
2471
  if (TEST_PLAN_UNIT_TESTS_ONLY_RE.test(region))
2262
2472
  return { status: "pass", errors: [] };
2263
2473
  if (/\*\*\[(?:agent|principal)\]\*\*/.test(region))
2264
2474
  return { status: "pass", errors: [] };
2475
+ if (located.found && extractFencedBlocks(region).length > 0)
2476
+ return { status: "pass", errors: [] };
2265
2477
  return {
2266
2478
  status: "fail",
2267
2479
  errors: [
2268
- "brief-validation Test Plan: no Test Plan section found — expected `Test Plan: unit-tests-only`, or at least one `**[agent]**`/`**[principal]**`-tagged checklist item."
2480
+ "brief-validation Test Plan: no Test Plan section found — expected `Test Plan: unit-tests-only`, a fenced `[agent]` command list, or at least one `**[agent]**`/`**[principal]**`-tagged checklist item."
2269
2481
  ]
2270
2482
  };
2271
2483
  }
@@ -2388,8 +2600,162 @@ function isBriefShaped(prBody) {
2388
2600
  const stripped = stripCode(prBody);
2389
2601
  return BRIEF_SHAPE_MARKERS.filter((check) => check(stripped).status === "pass").length >= 2;
2390
2602
  }
2603
+ var COMMAND_WORDS = ["export", "bun", "gh", "git", "grep", "sed", "cat", "diff", "vinaya"];
2604
+ var BRIEF_RULES_SINCE_PR = 394;
2605
+ var AGENT_BOXES_REFUSED_SINCE_PR = 396;
2606
+ var AGENT_BOX_LINE_RE = /^-\s*\[[ xX]\]\s*\*{2}\[agent\]\*{2}/im;
2607
+ function checkNoAgentBoxes(prBody) {
2608
+ const region = testPlanRegion(prBody);
2609
+ if (!AGENT_BOX_LINE_RE.test(region))
2610
+ return { status: "pass", errors: [] };
2611
+ return {
2612
+ status: "fail",
2613
+ errors: [
2614
+ "brief-validation no agent boxes: the Test Plan carries a checkbox `[agent]` item — an agent never ticks a box or edits a PR body (Principal ruling, 2026-09-03, after PR #395). Render the `[agent]` half as a fenced command list instead; `vinaya pr report` runs each command from the PR head and writes its actual output into the AEG:EVIDENCE block. `[principal]` checkboxes are unaffected."
2615
+ ]
2616
+ };
2617
+ }
2618
+ function rolloutThresholdFor(message) {
2619
+ if (message.startsWith("brief-validation unpinned code claim:") || message.startsWith("brief-validation commands carry output:") || message.startsWith("brief-validation consumer tests:") || message.startsWith("brief-validation defeat cases:")) {
2620
+ return BRIEF_RULES_SINCE_PR;
2621
+ }
2622
+ if (message.startsWith("brief-validation no agent boxes:"))
2623
+ return AGENT_BOXES_REFUSED_SINCE_PR;
2624
+ return null;
2625
+ }
2626
+ function partitionBriefErrorsByRollout(errors, prNumber) {
2627
+ const blocking = [];
2628
+ const info = [];
2629
+ for (const e of errors) {
2630
+ const threshold = rolloutThresholdFor(e);
2631
+ const grandfathered = threshold !== null && prNumber !== null && prNumber < threshold;
2632
+ (grandfathered ? info : blocking).push(e);
2633
+ }
2634
+ return { blocking, info };
2635
+ }
2636
+ function extractFencedBlocks(text) {
2637
+ const re = /^[ \t]*(`{3,}|~{3,})([^\n]*)\n([\s\S]*?)^[ \t]*\1[ \t]*$/gm;
2638
+ const blocks = [];
2639
+ let m = re.exec(text);
2640
+ while (m !== null) {
2641
+ const lang = m[2].trim().toLowerCase().split(/\s+/)[0] ?? "";
2642
+ blocks.push({ start: m.index, end: m.index + m[0].length, lang, content: m[3] });
2643
+ m = re.exec(text);
2644
+ }
2645
+ return blocks;
2646
+ }
2647
+ function firstNonBlankLine(text) {
2648
+ const line = text.split(`
2649
+ `).find((l) => l.trim().length > 0);
2650
+ return line?.trim() ?? "";
2651
+ }
2652
+ var FILE_LINE_RE = /\b[\w./-]+\.(?:tsx?|jsx?|mjs|cjs|md|mdx|json|ya?ml|sh|py|toml):\d+\b/g;
2653
+ function checkNoUnpinnedCodeClaims(prBody) {
2654
+ const premiseText = premiseBlockText(prBody);
2655
+ const withoutPremise = premiseText ? prBody.replace(premiseText, "") : prBody;
2656
+ const scanned = stripCode(withoutPremise, { inlineSpans: "keep" });
2657
+ const matches = scanned.match(FILE_LINE_RE) ?? [];
2658
+ if (matches.length === 0)
2659
+ return { status: "pass", errors: [] };
2660
+ return {
2661
+ status: "fail",
2662
+ errors: matches.map((m) => `brief-validation unpinned code claim: "${m}" states a code fact by file-and-line reference outside a \`Premise:\` pin and outside a fenced code block — pin it in \`Premise:\`, or show the executed command and its output instead.`)
2663
+ };
2664
+ }
2665
+ function extractNumberedSection(prBody, num) {
2666
+ const startRe = new RegExp(`^#{1,4}\\s*(?:\\*\\*)?${num}[a-z]?\\.\\s`, "im");
2667
+ const start = startRe.exec(prBody);
2668
+ if (!start)
2669
+ return null;
2670
+ const afterStart = start.index + start[0].length;
2671
+ const rest = prBody.slice(afterStart);
2672
+ const next = /^#{1,4}\s/m.exec(rest);
2673
+ return next ? rest.slice(0, next.index) : rest;
2674
+ }
2675
+ function commandWordOf(line) {
2676
+ return line.trim().split(/\s+/)[0] ?? "";
2677
+ }
2678
+ function isCommandBlock(content) {
2679
+ return COMMAND_WORDS.includes(commandWordOf(firstNonBlankLine(content)));
2680
+ }
2681
+ function isStep0Block(content) {
2682
+ return firstNonBlankLine(content).startsWith("git worktree add");
2683
+ }
2684
+ function checkCommandsCarryOutput(prBody) {
2685
+ const errors = [];
2686
+ for (const [label2, num] of [
2687
+ ["5", 5],
2688
+ ["6", 6]
2689
+ ]) {
2690
+ const section = extractNumberedSection(prBody, num);
2691
+ if (!section)
2692
+ continue;
2693
+ const blocks = extractFencedBlocks(section);
2694
+ for (let i = 0;i < blocks.length; i++) {
2695
+ const block = blocks[i];
2696
+ if (isStep0Block(block.content) || !isCommandBlock(block.content))
2697
+ continue;
2698
+ const next = blocks[i + 1];
2699
+ if (!next || isCommandBlock(next.content)) {
2700
+ errors.push(`brief-validation commands carry output: §${label2} has a command block ("${firstNonBlankLine(block.content)}") not immediately followed by a non-command output block — the very next fenced block must hold that command's actual output, not another command.`);
2701
+ }
2702
+ }
2703
+ }
2704
+ return errors.length === 0 ? { status: "pass", errors: [] } : { status: "fail", errors };
2705
+ }
2706
+ var CONSUMER_TESTS_SENTINEL_RE = /consumer-tests\s*:\s*none\s*[-—–]\s*\S/i;
2707
+ function packagesNamedIn(text) {
2708
+ const re = /\bpackages\/([A-Za-z0-9_-]+)\//g;
2709
+ const pkgs = new Set;
2710
+ let m = re.exec(text);
2711
+ while (m !== null) {
2712
+ pkgs.add(m[1]);
2713
+ m = re.exec(text);
2714
+ }
2715
+ return [...pkgs];
2716
+ }
2717
+ function hasTestPathForConsumer(text, consumerDir) {
2718
+ const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2719
+ const re = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
2720
+ return re.test(text);
2721
+ }
2722
+ function checkConsumerTests(prBody, consumersOf) {
2723
+ const section4 = extractNumberedSection(prBody, 4);
2724
+ if (!section4)
2725
+ return { status: "pass", errors: [] };
2726
+ if (CONSUMER_TESTS_SENTINEL_RE.test(section4))
2727
+ return { status: "pass", errors: [] };
2728
+ const errors = [];
2729
+ for (const pkg of packagesNamedIn(section4)) {
2730
+ for (const consumerDir of consumersOf(pkg)) {
2731
+ if (hasTestPathForConsumer(section4, consumerDir))
2732
+ continue;
2733
+ errors.push(`brief-validation consumer tests: §4 names a path under packages/${pkg}/, and ${consumerDir} depends on @attalabs/${pkg}, but no test path under ${consumerDir} is named in §4 — name one, or add \`consumer-tests: none — <reason>\`.`);
2734
+ }
2735
+ }
2736
+ return errors.length === 0 ? { status: "pass", errors: [] } : { status: "fail", errors };
2737
+ }
2738
+ var CHECK_NAME_RE = /\bcheck-[a-z0-9-]+(?:\.ts)?\b/;
2739
+ var FORGE_WRITE_COMMAND_RE = /\bgh\s+(?:pr|issue)\s+(?:create|merge|close|comment|edit|review)\b|\bgit\s+push\b|\bvinaya\s+pr\s+(?:create|report\s+--write)\b/i;
2740
+ var DEFEAT_CASES_RE = /defeat cases\s*:/i;
2741
+ function checkDefeatCases(prBody) {
2742
+ const section4 = extractNumberedSection(prBody, 4);
2743
+ if (!section4)
2744
+ return { status: "pass", errors: [] };
2745
+ if (!CHECK_NAME_RE.test(section4) && !FORGE_WRITE_COMMAND_RE.test(section4))
2746
+ return { status: "pass", errors: [] };
2747
+ const section6 = extractNumberedSection(prBody, 6) ?? "";
2748
+ if (DEFEAT_CASES_RE.test(section6))
2749
+ return { status: "pass", errors: [] };
2750
+ return {
2751
+ status: "fail",
2752
+ errors: [
2753
+ "brief-validation defeat cases: §4 names a check or a forge-writing command, but §6 carries no `Defeat cases:` line — name the inputs that would defeat this check, or that this command must not accidentally trigger on."
2754
+ ]
2755
+ };
2756
+ }
2391
2757
  function checkBriefSections(prBody, readTier, options = {}) {
2392
- const { requireClosesN = true } = options;
2758
+ const { requireClosesN = true, consumersOf = () => [] } = options;
2393
2759
  const results = [
2394
2760
  checkTierField(prBody, readTier),
2395
2761
  checkTestPlan(prBody),
@@ -2402,10 +2768,219 @@ function checkBriefSections(prBody, readTier, options = {}) {
2402
2768
  checkAutonomyClause(prBody),
2403
2769
  checkProjectField(prBody),
2404
2770
  checkForField(prBody),
2771
+ checkNoUnpinnedCodeClaims(prBody),
2772
+ checkNoAgentBoxes(prBody),
2773
+ checkCommandsCarryOutput(prBody),
2774
+ checkConsumerTests(prBody, consumersOf),
2775
+ checkDefeatCases(prBody),
2405
2776
  ...requireClosesN ? [checkClosesN(prBody)] : []
2406
2777
  ];
2407
2778
  return { errors: results.flatMap((r) => r.errors) };
2408
2779
  }
2780
+ // ../../packages/aeg-core/src/doctrine-portability.ts
2781
+ var DEFAULT_SHIPS_PREFIX = "aeg-root/";
2782
+ var STATIC_PORTABLE_PREFIXES = [
2783
+ "roles/",
2784
+ "contracts/",
2785
+ "skills/",
2786
+ ".github/",
2787
+ ".vinaya/",
2788
+ ".claude/",
2789
+ ".git/",
2790
+ ".husky/"
2791
+ ];
2792
+ var EXEMPT_LITERALS = new Set([
2793
+ "path/inside/the/shipped/diff.ts",
2794
+ "path/inside/the/surface.ts",
2795
+ "apps/x/specs/..."
2796
+ ]);
2797
+ var CITED_PATH_PATTERN = /^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]*)+$/;
2798
+ var NON_PATH_TOP_SEGMENTS = new Set(["origin", "refs", "HEAD", "vinaya", "fix"]);
2799
+ var VENDOR_NAME_SOURCE = "\\bclaude code\\b|\\bclaude\\b|\\banthropic\\b|\\bchatgpt\\b|\\bopenai\\b|\\bgpt\\b|\\bgemini\\b|\\bcodex\\b|\\bgrok\\b|\\bdeepseek\\b|\\bopus\\b|\\bsonnet\\b|\\bhaiku\\b";
2800
+ var VENDOR_EXAMPLE_START = /<!--\s*AEG:VENDOR-EXAMPLE:START\s*-->/;
2801
+ var VENDOR_EXAMPLE_END = /<!--\s*AEG:VENDOR-EXAMPLE:END\s*-->/;
2802
+ function maskVendorExampleRegion(masked) {
2803
+ const start = VENDOR_EXAMPLE_START.exec(masked);
2804
+ if (!start)
2805
+ return masked;
2806
+ const afterStart = start.index + start[0].length;
2807
+ const end = VENDOR_EXAMPLE_END.exec(masked.slice(afterStart));
2808
+ if (!end)
2809
+ return masked;
2810
+ const regionEnd = afterStart + end.index + end[0].length;
2811
+ const region = masked.slice(start.index, regionEnd);
2812
+ const blanked = region.replace(/[^\n]/g, " ");
2813
+ return masked.slice(0, start.index) + blanked + masked.slice(regionEnd);
2814
+ }
2815
+ function extractVendorMentions(content) {
2816
+ const scoped = maskVendorExampleRegion(maskCode(content));
2817
+ const found = [];
2818
+ const pattern = new RegExp(VENDOR_NAME_SOURCE, "gi");
2819
+ let match = pattern.exec(scoped);
2820
+ while (match !== null) {
2821
+ found.push({ name: match[0], index: match.index });
2822
+ match = pattern.exec(scoped);
2823
+ }
2824
+ return found;
2825
+ }
2826
+ function extractCitedPaths(content) {
2827
+ const found = [];
2828
+ const spanPattern = /`([^`\n]+)`/g;
2829
+ let match = spanPattern.exec(content);
2830
+ while (match !== null) {
2831
+ const cited = match[1] ?? "";
2832
+ const topSegment = cited.slice(0, cited.indexOf("/"));
2833
+ if (CITED_PATH_PATTERN.test(cited) && !NON_PATH_TOP_SEGMENTS.has(topSegment)) {
2834
+ found.push({ cited, index: match.index });
2835
+ }
2836
+ match = spanPattern.exec(content);
2837
+ }
2838
+ return found;
2839
+ }
2840
+ function lineAtIndex(content, index) {
2841
+ let line = 1;
2842
+ for (let i = 0;i < index; i++) {
2843
+ if (content.charCodeAt(i) === 10)
2844
+ line++;
2845
+ }
2846
+ return line;
2847
+ }
2848
+ function isPortable(cited, shipsPrefix) {
2849
+ if (cited.startsWith(shipsPrefix))
2850
+ return true;
2851
+ return STATIC_PORTABLE_PREFIXES.some((prefix) => cited.startsWith(prefix));
2852
+ }
2853
+ function checkDoctrinePortability(files, shipsPrefix = DEFAULT_SHIPS_PREFIX) {
2854
+ const findings = [];
2855
+ for (const file of files) {
2856
+ if (!file.path.startsWith(shipsPrefix))
2857
+ continue;
2858
+ for (const { cited, index } of extractCitedPaths(file.content)) {
2859
+ if (EXEMPT_LITERALS.has(cited))
2860
+ continue;
2861
+ if (isPortable(cited, shipsPrefix))
2862
+ continue;
2863
+ findings.push({
2864
+ file: file.path,
2865
+ line: lineAtIndex(file.content, index),
2866
+ cited,
2867
+ message: `cites "${cited}", a path that only exists in the authoring repository — not portable doctrine`,
2868
+ kind: "path"
2869
+ });
2870
+ }
2871
+ for (const { name, index } of extractVendorMentions(file.content)) {
2872
+ findings.push({
2873
+ file: file.path,
2874
+ line: lineAtIndex(file.content, index),
2875
+ cited: name,
2876
+ message: `names "${name}" outside the one fenced vendor-example home (an <!-- AEG:VENDOR-EXAMPLE:START --> … <!-- AEG:VENDOR-EXAMPLE:END --> pair) — portable doctrine refers to a host generically everywhere else`,
2877
+ kind: "vendor-name"
2878
+ });
2879
+ }
2880
+ }
2881
+ return findings;
2882
+ }
2883
+
2884
+ // ../../packages/aeg-core/src/doctrine-no-procedures.ts
2885
+ function vendorExampleSpan(content) {
2886
+ const start = VENDOR_EXAMPLE_START.exec(content);
2887
+ if (!start)
2888
+ return null;
2889
+ const afterStart = start.index + start[0].length;
2890
+ const end = VENDOR_EXAMPLE_END.exec(content.slice(afterStart));
2891
+ if (!end)
2892
+ return null;
2893
+ return { start: start.index, end: afterStart + end.index + end[0].length };
2894
+ }
2895
+ function isCommandLine(line) {
2896
+ const word = line.trim().split(/\s+/)[0] ?? "";
2897
+ return COMMAND_WORDS.includes(word);
2898
+ }
2899
+ var SHELL_LANGS = new Set(["", "sh", "bash", "shell", "console"]);
2900
+ function procedureLineNumberAt(content, index) {
2901
+ return content.slice(0, index).split(`
2902
+ `).length;
2903
+ }
2904
+ function isTemplatePath(path) {
2905
+ return /(^|\/)templates\//.test(path);
2906
+ }
2907
+ function checkDoctrineNoProcedures(files) {
2908
+ const findings = [];
2909
+ for (const file of files) {
2910
+ if (isTemplatePath(file.path))
2911
+ continue;
2912
+ const span = vendorExampleSpan(file.content);
2913
+ for (const block of extractFencedBlocks(file.content)) {
2914
+ if (span && block.start >= span.start && block.end <= span.end)
2915
+ continue;
2916
+ if (!SHELL_LANGS.has(block.lang))
2917
+ continue;
2918
+ const commandLineCount = block.content.split(`
2919
+ `).filter(isCommandLine).length;
2920
+ if (commandLineCount >= 2) {
2921
+ findings.push({
2922
+ file: file.path,
2923
+ line: procedureLineNumberAt(file.content, block.start),
2924
+ message: `this fenced block has ${commandLineCount} shell-command lines — this sequence is a \`vinaya\` command, name it (or move it inside the \`AEG:VENDOR-EXAMPLE\` anchor / a \`templates/\` file).`
2925
+ });
2926
+ }
2927
+ }
2928
+ }
2929
+ return findings;
2930
+ }
2931
+ // ../../packages/aeg-core/src/pr-report-density.ts
2932
+ function headingSectionBody(prBody, heading) {
2933
+ const headingRe = new RegExp(`^##\\s+${heading}\\s*$`, "im");
2934
+ const m = headingRe.exec(prBody);
2935
+ if (!m)
2936
+ return null;
2937
+ const start = m.index + m[0].length;
2938
+ const rest = prBody.slice(start);
2939
+ const nextHeading = /^#{1,2}\s/m.exec(rest);
2940
+ return nextHeading ? rest.slice(0, nextHeading.index) : rest;
2941
+ }
2942
+ function stripAnchorBlocks(sectionText) {
2943
+ let result = sectionText;
2944
+ for (const field of ["CLOSES", "PROJECT", "TIER", "PREMISE", "TEST-PLAN", "EVIDENCE"]) {
2945
+ for (;; ) {
2946
+ const bounds = anchoredRegionBounds(result, field);
2947
+ if (!bounds)
2948
+ break;
2949
+ result = result.slice(0, bounds.outerStart) + result.slice(bounds.outerEnd);
2950
+ }
2951
+ }
2952
+ return result;
2953
+ }
2954
+ function paragraphCount(sectionText) {
2955
+ const stripped = stripCode(stripAnchorBlocks(sectionText)).trim();
2956
+ if (stripped.length === 0)
2957
+ return 0;
2958
+ return stripped.split(/\n[ \t]*\n/).map((block) => block.trim()).filter((block) => block.length > 0).length;
2959
+ }
2960
+ function checkSectionDensity(prBody, heading) {
2961
+ const body = headingSectionBody(prBody, heading);
2962
+ if (body === null)
2963
+ return { status: "pass", errors: [] };
2964
+ const count = paragraphCount(body);
2965
+ if (count <= 1)
2966
+ return { status: "pass", errors: [] };
2967
+ return {
2968
+ status: "fail",
2969
+ errors: [
2970
+ `pr-report-density ${heading}: "## ${heading}" holds ${count} paragraphs — the canonical PR-report form (aeg-root/roles/developer.md § PR body) requires exactly one. Collapse it to one paragraph; move the rest into a section of your own below the canonical four ("Add anything you want beneath the four sections", developer.md), a commit message, or the changeset — never into the AEG:EVIDENCE block, which is emitted only, never hand-typed.`
2971
+ ]
2972
+ };
2973
+ }
2974
+ function checkSummaryDensity(prBody) {
2975
+ return checkSectionDensity(prBody, "Summary");
2976
+ }
2977
+ function checkScopeDensity(prBody) {
2978
+ return checkSectionDensity(prBody, "Scope");
2979
+ }
2980
+ function checkPrReportDensity(prBody) {
2981
+ const results = [checkSummaryDensity(prBody), checkScopeDensity(prBody)];
2982
+ return { errors: results.flatMap((r) => r.errors) };
2983
+ }
2409
2984
  // ../../packages/aeg-core/src/issue-validation.ts
2410
2985
  function hasRationaleField(body, labelPattern) {
2411
2986
  const re = new RegExp(`(?:\\*\\*|^#{1,4}\\s+)\\s*(?:${labelPattern})`, "im");
@@ -2657,6 +3232,17 @@ function checkD1(entries, issueToEntry, taskToEntry) {
2657
3232
  const depEntry = resolveDepEntry(dep, e.trancheSlug, issueToEntry, taskToEntry);
2658
3233
  if (!depEntry)
2659
3234
  continue;
3235
+ const sameTask = depEntry.trancheSlug === e.trancheSlug && depEntry.task.id === e.task.id;
3236
+ const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
3237
+ if (sameTask || sameIssue) {
3238
+ failures.push({
3239
+ issue: e.task.issue,
3240
+ tranche: e.trancheSlug,
3241
+ task: e.task.id,
3242
+ reason: `INTERNAL: parsed a self-dependency (depends-on ${dep}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream.`
3243
+ });
3244
+ continue;
3245
+ }
2660
3246
  const depFacts = depEntry.facts;
2661
3247
  const depClosed = depFacts?.issueState === "closed";
2662
3248
  if (!depClosed) {
@@ -2800,41 +3386,24 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
2800
3386
  }
2801
3387
  return { ok: true, expectedIssue };
2802
3388
  }
2803
- // ../../packages/aeg-core/src/verdict-extraction.ts
2804
- var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
2805
- function extractHeadSha(comment) {
2806
- const m = comment.match(HEAD_SHA_PATTERN);
2807
- return m ? m[1].toLowerCase() : null;
2808
- }
2809
- function extractVerdict(comments, valuePattern, missingLabel) {
2810
- const clearHits = comments.filter((c) => valuePattern.test(c));
2811
- if (clearHits.length > 0) {
2812
- const latest = clearHits[clearHits.length - 1];
2813
- const m = latest.match(valuePattern);
2814
- return {
2815
- value: m[1].toUpperCase().replace(/[_-]/g, " "),
2816
- headSha: extractHeadSha(latest),
2817
- danglingNote: null
2818
- };
2819
- }
2820
- return {
2821
- value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
2822
- headSha: null,
2823
- danglingNote: `no ${missingLabel} verdict comment found on this PR`
2824
- };
2825
- }
2826
- function extractCodeReviewVerdict(comments) {
2827
- return extractVerdict(comments, /^[ \t]*(?:\*{1,3}|_{1,3})?VERDICT:\s*(APPROVE|REQUEST[ _-]?CHANGES|LGTM)(?![A-Za-z0-9])/im, "code-reviewer");
2828
- }
2829
- function extractSecurityReviewVerdict(comments) {
2830
- return extractVerdict(comments, /^[ \t]*(?:\*{1,3}|_{1,3})?VERDICT:\s*(PASS|FAIL)(?![A-Za-z0-9])/im, "security-review");
2831
- }
2832
3389
  // ../../packages/aeg-core/src/review-gate.ts
2833
3390
  function isBoundToHead(extraction, headSha) {
2834
3391
  if (!extraction.headSha)
2835
3392
  return false;
2836
3393
  return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
2837
3394
  }
3395
+ function isBoundByPatchIdentity(extraction, headSha, patchIdOf) {
3396
+ if (patchIdOf === undefined || !extraction.headSha)
3397
+ return false;
3398
+ const judged = patchIdOf(extraction.headSha);
3399
+ const current = patchIdOf(headSha);
3400
+ if (judged === null || current === null)
3401
+ return false;
3402
+ return judged === current;
3403
+ }
3404
+ function isBoundToPatch(extraction, headSha, patchIdOf) {
3405
+ return isBoundToHead(extraction, headSha) || isBoundByPatchIdentity(extraction, headSha, patchIdOf);
3406
+ }
2838
3407
  function checkReviewGate(input) {
2839
3408
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
2840
3409
  const waived = isWaiverLabelActorVerified({
@@ -2850,6 +3419,8 @@ function checkReviewGate(input) {
2850
3419
  waived: true
2851
3420
  };
2852
3421
  }
3422
+ const reportedMechanicalChecks = input.mechanicalChecks.filter((c) => c.bucket !== "skipping" && c.bucket !== "neutral");
3423
+ const mechanicalChecksClean = reportedMechanicalChecks.length > 0 && reportedMechanicalChecks.every((c) => c.bucket === "pass");
2853
3424
  const verified = input.comments.filter((c) => isPrincipal(c.author, principalAllowlist));
2854
3425
  const ignoredCount = input.comments.filter((c) => !isPrincipal(c.author, principalAllowlist) && c.body.includes("VERDICT")).length;
2855
3426
  const verifiedBodies = verified.map((c) => c.body);
@@ -2857,12 +3428,12 @@ function checkReviewGate(input) {
2857
3428
  const security = extractSecurityReviewVerdict(verifiedBodies);
2858
3429
  const codeReviewClean = codeReview.value === "APPROVE";
2859
3430
  const securityClean = security.value === "PASS";
2860
- const codeReviewBound = isBoundToHead(codeReview, input.headSha);
2861
- const securityBound = isBoundToHead(security, input.headSha);
2862
- if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
3431
+ const codeReviewBound = isBoundToPatch(codeReview, input.headSha, input.patchIdOf);
3432
+ const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
3433
+ if (codeReviewClean && codeReviewBound && securityClean && securityBound && mechanicalChecksClean) {
2863
3434
  return {
2864
3435
  verdict: "pass",
2865
- reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
3436
+ reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}, and every reported mechanical check is green.`,
2866
3437
  waived: false
2867
3438
  };
2868
3439
  }
@@ -2877,6 +3448,9 @@ function checkReviewGate(input) {
2877
3448
  } else if (!securityBound) {
2878
3449
  problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
2879
3450
  }
3451
+ if (!mechanicalChecksClean) {
3452
+ problems.push(reportedMechanicalChecks.length === 0 ? "no mechanical checks have reported for this head yet" : `mechanical check(s) not green: ${reportedMechanicalChecks.filter((c) => c.bucket !== "pass").map((c) => `${c.name} (${c.bucket})`).join(", ")}`);
3453
+ }
2880
3454
  const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
2881
3455
  return {
2882
3456
  verdict: "fail",
@@ -3156,72 +3730,6 @@ function compareToBaseline(current, baseline) {
3156
3730
  perTool
3157
3731
  };
3158
3732
  }
3159
- // ../../packages/aeg-core/src/doctrine-portability.ts
3160
- var DEFAULT_SHIPS_PREFIX = "aeg-root/";
3161
- var STATIC_PORTABLE_PREFIXES = [
3162
- "roles/",
3163
- "contracts/",
3164
- "skills/",
3165
- ".github/",
3166
- ".vinaya/",
3167
- ".claude/",
3168
- ".git/",
3169
- ".husky/"
3170
- ];
3171
- var EXEMPT_LITERALS = new Set([
3172
- "path/inside/the/shipped/diff.ts",
3173
- "path/inside/the/surface.ts",
3174
- "apps/x/specs/..."
3175
- ]);
3176
- var CITED_PATH_PATTERN = /^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]*)+$/;
3177
- var NON_PATH_TOP_SEGMENTS = new Set(["origin", "refs", "HEAD", "vinaya", "fix"]);
3178
- function extractCitedPaths(content) {
3179
- const found = [];
3180
- const spanPattern = /`([^`\n]+)`/g;
3181
- let match = spanPattern.exec(content);
3182
- while (match !== null) {
3183
- const cited = match[1] ?? "";
3184
- const topSegment = cited.slice(0, cited.indexOf("/"));
3185
- if (CITED_PATH_PATTERN.test(cited) && !NON_PATH_TOP_SEGMENTS.has(topSegment)) {
3186
- found.push({ cited, index: match.index });
3187
- }
3188
- match = spanPattern.exec(content);
3189
- }
3190
- return found;
3191
- }
3192
- function lineAtIndex(content, index) {
3193
- let line = 1;
3194
- for (let i = 0;i < index; i++) {
3195
- if (content.charCodeAt(i) === 10)
3196
- line++;
3197
- }
3198
- return line;
3199
- }
3200
- function isPortable(cited, shipsPrefix) {
3201
- if (cited.startsWith(shipsPrefix))
3202
- return true;
3203
- return STATIC_PORTABLE_PREFIXES.some((prefix) => cited.startsWith(prefix));
3204
- }
3205
- function checkDoctrinePortability(files, shipsPrefix = DEFAULT_SHIPS_PREFIX) {
3206
- const findings = [];
3207
- for (const file of files) {
3208
- if (!file.path.startsWith(shipsPrefix))
3209
- continue;
3210
- for (const { cited, index } of extractCitedPaths(file.content)) {
3211
- if (EXEMPT_LITERALS.has(cited))
3212
- continue;
3213
- if (isPortable(cited, shipsPrefix))
3214
- continue;
3215
- findings.push({
3216
- file: file.path,
3217
- line: lineAtIndex(file.content, index),
3218
- cited,
3219
- message: `cites "${cited}", a path that only exists in the authoring repository — not portable doctrine`
3220
- });
3221
- }
3222
- }
3223
- return findings;
3224
- }
3225
3733
  // ../../packages/aeg-core/src/reader-resolvable-prose.ts
3226
3734
  var SHIPS_PREFIX = "aeg-root/";
3227
3735
  function shipsArchivePrefix(shipsPrefix) {
@@ -3557,6 +4065,12 @@ function scanRetiredVocabulary(files) {
3557
4065
  function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
3558
4066
  return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
3559
4067
  }
4068
+ function isSelfDependency(dep, task, issue) {
4069
+ if (dep.issue !== null && issue !== null && dep.issue === issue.number)
4070
+ return true;
4071
+ const bare = dep.id.trim().replace(/^#/, "");
4072
+ return bare.toLowerCase() === task.id.trim().toLowerCase();
4073
+ }
3560
4074
  function checkDispatchReadiness(input) {
3561
4075
  const { trancheSlug, task } = input;
3562
4076
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
@@ -3571,6 +4085,11 @@ function checkDispatchReadiness(input) {
3571
4085
  blockers.push(`dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
3572
4086
  }
3573
4087
  for (const dep of input.dependsOn) {
4088
+ if (isSelfDependency(dep, task, input.issue)) {
4089
+ const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
4090
+ blockers.push(`dispatch-gate INTERNAL: parsed a self-dependency for task ${task.id} (${issueStr}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream. Re-run once the rationale is corrected or the fix ships.`);
4091
+ continue;
4092
+ }
3574
4093
  if (dep.resolved === false) {
3575
4094
  blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on "${dep.id}", which is UNRESOLVABLE — the resolver could not find a matching tranche/task/Issue for this edge (not a claim about merge status). Not dispatchable until the edge is corrected.`);
3576
4095
  continue;
@@ -3810,27 +4329,27 @@ function evaluateTestPlanGate(body, branch) {
3810
4329
  };
3811
4330
  }
3812
4331
  const checkboxLines = section.split(`
3813
- `).map((line) => line.trimStart()).filter((line) => /^[-*]\s+\[[ xX]\]/.test(line));
4332
+ `).map((line) => line.trimStart()).filter((line) => /^[-*]\s+\[[ xX]\]\s*\*{0,2}\[principal\]\*{0,2}/i.test(line));
3814
4333
  if (checkboxLines.length === 0) {
3815
4334
  return {
3816
4335
  verdict: "pass",
3817
4336
  messages: [
3818
- "Test Plan section found but has no checkbox items.",
4337
+ "Test Plan section found but has no `[principal]` checkbox items.",
3819
4338
  "PASS (advisory) — the section may be empty or expressed in prose; verification is the Principal's call."
3820
4339
  ]
3821
4340
  };
3822
4341
  }
3823
4342
  const unchecked = checkboxLines.filter((line) => /^[-*]\s+\[\s\]/.test(line));
3824
4343
  const checked = checkboxLines.filter((line) => /^[-*]\s+\[[xX]\]/.test(line));
3825
- const messages = [`Test Plan items: ${checked.length} ticked, ${unchecked.length} unticked.`];
4344
+ const messages = [`Test Plan [principal] items: ${checked.length} ticked, ${unchecked.length} unticked.`];
3826
4345
  if (unchecked.length > 0) {
3827
- messages.push("", "FAIL — the following Test Plan items are unticked:");
4346
+ messages.push("", "FAIL — the following [principal] Test Plan items are unticked:");
3828
4347
  for (const line of unchecked)
3829
4348
  messages.push(` ${line}`);
3830
- messages.push("", "Per aeg-root/roles/developer.md (Verification), a PR is not mergeable while any Test Plan box is unticked.", "- [agent] items: the Developer-agent posts the actual command output as evidence and ticks the box.", "- [principal] items: the Principal runs the item in a real signed-in browser and ticks the box.", "Note: editing the PR body does not retrigger most workflows. If your PR body changes do not surface here, push an empty commit to re-run.");
4349
+ messages.push("", "Per aeg-root/roles/developer.md (Verification), a PR is not mergeable while any [principal] Test Plan box is unticked.", "The Principal runs the item in a real signed-in browser and ticks the box.", "Note: editing the PR body does not retrigger most workflows. If your PR body changes do not surface here, push an empty commit to re-run.");
3831
4350
  return { verdict: "fail", messages };
3832
4351
  }
3833
- messages.push("All Test Plan items ticked — runtime verification complete.", "PASS.");
4352
+ messages.push("All [principal] Test Plan items ticked — runtime verification complete.", "PASS.");
3834
4353
  return { verdict: "pass", messages };
3835
4354
  }
3836
4355
  // ../../packages/aeg-core/src/workspace-escape.ts
@@ -3932,7 +4451,7 @@ function main() {
3932
4451
  check: CHECK_NAME,
3933
4452
  severity: "error",
3934
4453
  message,
3935
- agent_recovery_prompt: "Open the PR body, tick every `[agent]` Test Plan box after actually running it (with pasted evidence), and leave `[principal]` boxes for the Principal, then re-run `vinaya check test-plan`."
4454
+ agent_recovery_prompt: "Run each `[agent]` Test Plan command from the PR head and let `vinaya pr report` write its output into the AEG:EVIDENCE block; leave `[principal]` boxes for the Principal to tick after verifying in a real signed-in browser. Re-run `vinaya check test-plan` afterwards."
3936
4455
  });
3937
4456
  }
3938
4457
  process.exit(1);