@maycombhq/atticus 0.0.0-stage → 2.0.0-beta.3

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 (475) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +17 -2
  3. package/THIRD-PARTY-LICENSES.txt +2607 -0
  4. package/dist/bin.js +47 -0
  5. package/dist/chunk-23W7VWKR.js +2609 -0
  6. package/dist/chunk-2C5B464C.js +106 -0
  7. package/dist/chunk-2LLFKP7C.js +53 -0
  8. package/dist/chunk-2MD7TXSG.js +166 -0
  9. package/dist/chunk-2UIQBDJO.js +131 -0
  10. package/dist/chunk-32MPK524.js +378 -0
  11. package/dist/chunk-3ALD2W2A.js +95 -0
  12. package/dist/chunk-3G4YI4ZU.js +16 -0
  13. package/dist/chunk-4AK7IBVP.js +915 -0
  14. package/dist/chunk-4BMDGTED.js +69 -0
  15. package/dist/chunk-4NDAAFQT.js +103 -0
  16. package/dist/chunk-4Q3NN7VF.js +32 -0
  17. package/dist/chunk-4RS5KZQD.js +25 -0
  18. package/dist/chunk-4SKZDI62.js +22 -0
  19. package/dist/chunk-55YODD3N.js +385 -0
  20. package/dist/chunk-56YQ6ROQ.js +3479 -0
  21. package/dist/chunk-5A5L3T2U.js +583 -0
  22. package/dist/chunk-5FEOD73Z.js +374 -0
  23. package/dist/chunk-5GDRCDQB.js +32 -0
  24. package/dist/chunk-5WS2YOGD.js +95 -0
  25. package/dist/chunk-65NYPGRC.js +766 -0
  26. package/dist/chunk-6BVONWJ7.js +50 -0
  27. package/dist/chunk-6D4FRTFF.js +53 -0
  28. package/dist/chunk-6GPJOFV4.js +149 -0
  29. package/dist/chunk-6JCPI55B.js +92 -0
  30. package/dist/chunk-6JUBZBMW.js +56 -0
  31. package/dist/chunk-6SCVQZX5.js +2522 -0
  32. package/dist/chunk-6TACQN3N.js +41 -0
  33. package/dist/chunk-72TXNLOC.js +496 -0
  34. package/dist/chunk-76WKMA4J.js +14 -0
  35. package/dist/chunk-7EFILTCI.js +67 -0
  36. package/dist/chunk-7EKXJZ2Y.js +2902 -0
  37. package/dist/chunk-7EOBHUMC.js +133 -0
  38. package/dist/chunk-7JUKDRSZ.js +1726 -0
  39. package/dist/chunk-7NN5VWDC.js +43 -0
  40. package/dist/chunk-7UFQZXZA.js +5949 -0
  41. package/dist/chunk-A3KPZJ5M.js +131 -0
  42. package/dist/chunk-A6AC72A5.js +48 -0
  43. package/dist/chunk-A7KZKIZX.js +166 -0
  44. package/dist/chunk-ABWHYWNI.js +101 -0
  45. package/dist/chunk-AFFIK6JI.js +46 -0
  46. package/dist/chunk-AHOQZ4OK.js +37 -0
  47. package/dist/chunk-AVJDSJ2F.js +18 -0
  48. package/dist/chunk-AXXCPAP6.js +203 -0
  49. package/dist/chunk-AY3QXYMB.js +41 -0
  50. package/dist/chunk-B2OKWJIU.js +143 -0
  51. package/dist/chunk-B632FH3I.js +251 -0
  52. package/dist/chunk-BLW4SXCU.js +38 -0
  53. package/dist/chunk-BVS2K73A.js +33 -0
  54. package/dist/chunk-BWZ77RQT.js +25 -0
  55. package/dist/chunk-BYBUDX4T.js +163 -0
  56. package/dist/chunk-BYZCY6SP.js +230 -0
  57. package/dist/chunk-C6EKFH7J.js +169 -0
  58. package/dist/chunk-C6MZFGN7.js +271 -0
  59. package/dist/chunk-CAQZUNOH.js +1150 -0
  60. package/dist/chunk-CD4XHYGY.js +856 -0
  61. package/dist/chunk-CEZQOLEO.js +1217 -0
  62. package/dist/chunk-CHWTETW7.js +128 -0
  63. package/dist/chunk-CMV7TGU6.js +31 -0
  64. package/dist/chunk-CNUKCS2T.js +2741 -0
  65. package/dist/chunk-CP3L3FH7.js +549 -0
  66. package/dist/chunk-CPGCA6GZ.js +41 -0
  67. package/dist/chunk-CWM6KM5G.js +315 -0
  68. package/dist/chunk-CXDSTRUJ.js +873 -0
  69. package/dist/chunk-D3PO4DOZ.js +261 -0
  70. package/dist/chunk-DFBCSHVT.js +69 -0
  71. package/dist/chunk-DHPACDG7.js +104 -0
  72. package/dist/chunk-DPC5Y5DL.js +3898 -0
  73. package/dist/chunk-DPUYDDQG.js +164 -0
  74. package/dist/chunk-DQN7TF5D.js +325 -0
  75. package/dist/chunk-EADP3PEN.js +450 -0
  76. package/dist/chunk-ED2LWWIP.js +878 -0
  77. package/dist/chunk-EHVTK3UE.js +116 -0
  78. package/dist/chunk-EPTBZ3GP.js +101 -0
  79. package/dist/chunk-EXIMEJ2X.js +545 -0
  80. package/dist/chunk-EYWIDJFU.js +130 -0
  81. package/dist/chunk-F44OMYIN.js +54 -0
  82. package/dist/chunk-F7HYOUFF.js +287 -0
  83. package/dist/chunk-FALXWAMD.js +182 -0
  84. package/dist/chunk-FBNPE7HS.js +49 -0
  85. package/dist/chunk-FSWUD5QB.js +108 -0
  86. package/dist/chunk-FVQISWPW.js +296 -0
  87. package/dist/chunk-G2B4ZQGH.js +240 -0
  88. package/dist/chunk-GK6XK5SP.js +1227 -0
  89. package/dist/chunk-GLEBK5L3.js +34 -0
  90. package/dist/chunk-GNB5QDDI.js +25 -0
  91. package/dist/chunk-GQ4N4WKF.js +1142 -0
  92. package/dist/chunk-GRMR5TRQ.js +119 -0
  93. package/dist/chunk-HC6XKREB.js +334 -0
  94. package/dist/chunk-HFQCFSJR.js +60 -0
  95. package/dist/chunk-HJDICQA4.js +102 -0
  96. package/dist/chunk-HLJAXMM4.js +233 -0
  97. package/dist/chunk-HVIMMDVB.js +162 -0
  98. package/dist/chunk-HZVLAPN5.js +32 -0
  99. package/dist/chunk-I3E7CX4H.js +155 -0
  100. package/dist/chunk-IQFTZ37C.js +2919 -0
  101. package/dist/chunk-ITY3VJRX.js +285 -0
  102. package/dist/chunk-JBTK5OP2.js +55 -0
  103. package/dist/chunk-JPGCERE2.js +26 -0
  104. package/dist/chunk-JQDNHIBC.js +38 -0
  105. package/dist/chunk-JRBCWLOR.js +307 -0
  106. package/dist/chunk-JRZQA2JM.js +212 -0
  107. package/dist/chunk-KCBJCA66.js +45 -0
  108. package/dist/chunk-KJPMEAI4.js +705 -0
  109. package/dist/chunk-KQDZXGNR.js +14 -0
  110. package/dist/chunk-KUUQR4HA.js +264 -0
  111. package/dist/chunk-L24C55VN.js +7923 -0
  112. package/dist/chunk-L3BX5ZIB.js +400 -0
  113. package/dist/chunk-L6SGQIR3.js +471 -0
  114. package/dist/chunk-LBENYT3H.js +66 -0
  115. package/dist/chunk-LNAJW6FB.js +47 -0
  116. package/dist/chunk-MC3H7736.js +19910 -0
  117. package/dist/chunk-MMOSZPWJ.js +273 -0
  118. package/dist/chunk-MXCHWULV.js +285 -0
  119. package/dist/chunk-N3E56YPN.js +344 -0
  120. package/dist/chunk-NHFQMOVK.js +180 -0
  121. package/dist/chunk-NQU6KNQF.js +27 -0
  122. package/dist/chunk-OIK5YPTN.js +391 -0
  123. package/dist/chunk-OLWFG75W.js +4330 -0
  124. package/dist/chunk-ONPUIE7Y.js +29 -0
  125. package/dist/chunk-OW7D2PJL.js +376 -0
  126. package/dist/chunk-P63VL4HE.js +2357 -0
  127. package/dist/chunk-PA4YNUMU.js +45 -0
  128. package/dist/chunk-PAMPPUR6.js +4500 -0
  129. package/dist/chunk-PBS3OFNN.js +113 -0
  130. package/dist/chunk-PFT3VTS4.js +214 -0
  131. package/dist/chunk-PFTA6BZF.js +49 -0
  132. package/dist/chunk-PNN5KLAY.js +119 -0
  133. package/dist/chunk-PRPPGYZG.js +104 -0
  134. package/dist/chunk-PUKEZ37F.js +666 -0
  135. package/dist/chunk-Q3HOFXZJ.js +76 -0
  136. package/dist/chunk-Q5AMFEWD.js +119 -0
  137. package/dist/chunk-QKRNMBJO.js +80 -0
  138. package/dist/chunk-QSU47IQ5.js +36 -0
  139. package/dist/chunk-QSXK2RGH.js +688 -0
  140. package/dist/chunk-QXNWYUYI.js +52 -0
  141. package/dist/chunk-R6RY4Q5T.js +51 -0
  142. package/dist/chunk-RCU3MPY2.js +1496 -0
  143. package/dist/chunk-RLIB4JUN.js +174 -0
  144. package/dist/chunk-RYSCLMU3.js +1991 -0
  145. package/dist/chunk-RZUZ4RJJ.js +415 -0
  146. package/dist/chunk-S2A7YC3Q.js +41433 -0
  147. package/dist/chunk-S2FBBX7D.js +540 -0
  148. package/dist/chunk-S34B4QPI.js +430 -0
  149. package/dist/chunk-SETWVXOJ.js +2078 -0
  150. package/dist/chunk-SMA4N4AC.js +211 -0
  151. package/dist/chunk-SP2I63OO.js +252 -0
  152. package/dist/chunk-STHSY74Q.js +242 -0
  153. package/dist/chunk-STUPFGXY.js +3206 -0
  154. package/dist/chunk-T5I4S2W3.js +34 -0
  155. package/dist/chunk-T6AIP7H5.js +3851 -0
  156. package/dist/chunk-TANFV3MZ.js +531 -0
  157. package/dist/chunk-TAVDMUV3.js +189 -0
  158. package/dist/chunk-TBSQJ6IV.js +324 -0
  159. package/dist/chunk-TIZHP7KE.js +408 -0
  160. package/dist/chunk-TV45EUGM.js +51 -0
  161. package/dist/chunk-TW2KPPUC.js +4087 -0
  162. package/dist/chunk-V2YTAIQO.js +1075 -0
  163. package/dist/chunk-V5PKHHQQ.js +1963 -0
  164. package/dist/chunk-VF4IOZZO.js +184 -0
  165. package/dist/chunk-VJTINHZI.js +48 -0
  166. package/dist/chunk-VKLILA2D.js +205 -0
  167. package/dist/chunk-VOGCSYOI.js +16 -0
  168. package/dist/chunk-VPTTCYOC.js +16101 -0
  169. package/dist/chunk-WY6PP6AA.js +53 -0
  170. package/dist/chunk-WYFIXP54.js +174 -0
  171. package/dist/chunk-WZ2BXIGG.js +34 -0
  172. package/dist/chunk-XJNDSYGO.js +147 -0
  173. package/dist/chunk-XPCM366L.js +70 -0
  174. package/dist/chunk-XSQKGVYA.js +69 -0
  175. package/dist/chunk-XUPVB7TL.js +207 -0
  176. package/dist/chunk-XZRON6IV.js +131 -0
  177. package/dist/chunk-Y4NG2FP5.js +94 -0
  178. package/dist/chunk-YEDDOYSC.js +27809 -0
  179. package/dist/chunk-YGMCXNTY.js +41 -0
  180. package/dist/chunk-YJKPTX6J.js +228 -0
  181. package/dist/chunk-YMZ3TNYB.js +18 -0
  182. package/dist/chunk-Z6LF7HQN.js +22 -0
  183. package/dist/chunk-Z7SG32ZY.js +55 -0
  184. package/dist/chunk-ZDPAS56H.js +76 -0
  185. package/dist/chunk-ZJSP24X4.js +1247 -0
  186. package/dist/chunk-ZMAINWBW.js +18 -0
  187. package/dist/chunk-ZMFPW66A.js +22 -0
  188. package/dist/chunk-ZUIAOKXI.js +1611 -0
  189. package/dist/chunk-ZVM6AKKM.js +47 -0
  190. package/dist/files.json +1888 -0
  191. package/dist/fixtures/forge/README.md +19 -0
  192. package/dist/fixtures/forge/expected/discrepancies.yaml +54 -0
  193. package/dist/fixtures/forge/expected/inputs.yaml +38 -0
  194. package/dist/fixtures/forge/expected/scan.yaml +342 -0
  195. package/dist/fixtures/forge/inputs/diagrams/forge-context.drawio +23 -0
  196. package/dist/fixtures/forge/inputs/partner-specs/northwind-pay-openapi.yaml +11 -0
  197. package/dist/fixtures/forge/inputs/tickets/ado-stories.json +4 -0
  198. package/dist/fixtures/forge/inputs/tickets/closed-service-requests.json +5 -0
  199. package/dist/fixtures/forge/inputs/wiki/integration-patterns.md +11 -0
  200. package/dist/fixtures/forge/inputs/wiki/ledger-solution-spec.md +9 -0
  201. package/dist/fixtures/forge/inputs/wiki/snow-firewall-request.md +4 -0
  202. package/dist/fixtures/forge/metarepo/judgments/reaches/84af5b6af24f7ca8c23013f3.yaml +17 -0
  203. package/dist/fixtures/forge/metarepo/judgments/reaches/c3917555edebddce8cecb647.yaml +17 -0
  204. package/dist/fixtures/forge/metarepo/judgments/reaches/c5fafd6601f8ca9e34e0bbd0.yaml +17 -0
  205. package/dist/fixtures/forge/metarepo/org-pack/agents/architecture-critic.yaml +9 -0
  206. package/dist/fixtures/forge/metarepo/org-pack/agents/story-writer.yaml +10 -0
  207. package/dist/fixtures/forge/metarepo/org-pack/checklists/architecture.yaml +115 -0
  208. package/dist/fixtures/forge/metarepo/org-pack/dialects/forge.yaml +111 -0
  209. package/dist/fixtures/forge/metarepo/org-pack/gates.yaml +22 -0
  210. package/dist/fixtures/forge/metarepo/org-pack/pack.yaml +11 -0
  211. package/dist/fixtures/forge/metarepo/org-pack/patterns/A.EB.PUB.yaml +12 -0
  212. package/dist/fixtures/forge/metarepo/org-pack/patterns/D.JDBC.yaml +12 -0
  213. package/dist/fixtures/forge/metarepo/org-pack/patterns/S.DIR.BFF.yaml +12 -0
  214. package/dist/fixtures/forge/metarepo/org-pack/patterns/S.GW.SOR.yaml +13 -0
  215. package/dist/fixtures/forge/metarepo/org-pack/patterns/S.GW.VND.yaml +14 -0
  216. package/dist/fixtures/forge/metarepo/org-pack/process/change-templates/feature.md +37 -0
  217. package/dist/fixtures/forge/metarepo/org-pack/process/skills/add-forge-endpoint/SKILL.md +17 -0
  218. package/dist/fixtures/forge/metarepo/org-pack/process/snow/firewall-rule.yaml +14 -0
  219. package/dist/fixtures/forge/metarepo/org-pack/process/snow/gateway-route.yaml +13 -0
  220. package/dist/fixtures/forge/metarepo/org-pack/profiles/forge/profile.yaml +12 -0
  221. package/dist/fixtures/forge/metarepo/org-pack/profiles/forge/repos.manifest.yaml +44 -0
  222. package/dist/fixtures/forge/metarepo/org-pack/roles.yaml +14 -0
  223. package/dist/fixtures/forge/metarepo/org-pack/runtime.yaml +14 -0
  224. package/dist/fixtures/forge/metarepo/org-pack/sources.yaml +3 -0
  225. package/dist/fixtures/forge/repos/forge-events/topics/payments.initiated.yaml +4 -0
  226. package/dist/fixtures/forge/repos/forge-events/topics/transfers.completed.yaml +4 -0
  227. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/ledger-v1.xml +3 -0
  228. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/policies/SpikeArrest.xml +3 -0
  229. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/policies/VerifyOAuth.xml +4 -0
  230. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/proxies/default.xml +14 -0
  231. package/dist/fixtures/forge/repos/forge-gateway/proxies/ledger-v1/apiproxy/targets/ledger.xml +5 -0
  232. package/dist/fixtures/forge/repos/forge-infra/alerts/ledger-api.yaml +5 -0
  233. package/dist/fixtures/forge/repos/forge-infra/k8s/ledger-api.yaml +15 -0
  234. package/dist/fixtures/forge/repos/forge-infra/k8s/portal-bff.yaml +7 -0
  235. package/dist/fixtures/forge/repos/forge-infra/terraform/ledger-db.tf +9 -0
  236. package/dist/fixtures/forge/repos/forge-ledger/api/ledger-openapi.yaml +31 -0
  237. package/dist/fixtures/forge/repos/forge-ledger/forge.yaml +4 -0
  238. package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/LedgerEndpoints.java +13 -0
  239. package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/LedgerEvents.java +11 -0
  240. package/dist/fixtures/forge/repos/forge-ledger/src/main/java/forge/ledger/NorthwindClient.java +10 -0
  241. package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/application.yml +3 -0
  242. package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/db/migration/V1__create_account.sql +5 -0
  243. package/dist/fixtures/forge/repos/forge-ledger/src/main/resources/db/migration/V2__create_entry.sql +7 -0
  244. package/dist/fixtures/forge/repos/forge-portal-bff/pom.xml +1 -0
  245. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/AuditPublisher.java +12 -0
  246. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/BalanceController.java +16 -0
  247. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/LedgerClient.java +13 -0
  248. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/java/forge/portal/PaymentEvents.java +13 -0
  249. package/dist/fixtures/forge/repos/forge-portal-bff/src/main/resources/application.yml +8 -0
  250. package/dist/fixtures/forge/repos/forge-portal-ui/package.json +1 -0
  251. package/dist/fixtures/forge/repos/forge-portal-ui/src/api/balance.ts +5 -0
  252. package/dist/fixtures/forge/repos/forge-reports-job/src/main/java/forge/reports/DailyEntriesJob.java +19 -0
  253. package/dist/fixtures/forge/repos/forge-reports-job/src/main/resources/application.yml +3 -0
  254. package/dist/fixtures/forge-change/README.md +41 -0
  255. package/dist/fixtures/forge-change/expected/findings.yaml +301 -0
  256. package/dist/fixtures/forge-change/expected/stories.yaml +101 -0
  257. package/dist/fixtures/forge-change/judgments/personal-data/0b5f3dbf5257a22275d8c74b.yaml +18 -0
  258. package/dist/fixtures/forge-change/judgments/personal-data/2759a66770134c417440bf2d.yaml +18 -0
  259. package/dist/fixtures/forge-change/judgments/personal-data/31b1ea2f14d0737f1aca79fd.yaml +18 -0
  260. package/dist/fixtures/forge-change/judgments/personal-data/49afd70c08228e65ad6ca1ed.yaml +18 -0
  261. package/dist/fixtures/forge-change/judgments/personal-data/761cabcb8a39f5e5866d2dd6.yaml +18 -0
  262. package/dist/fixtures/forge-change/judgments/personal-data/adae902b90cc8ffa0c765145.yaml +18 -0
  263. package/dist/fixtures/forge-change/judgments/personal-data/ba11bb933105ec9493e065dd.yaml +18 -0
  264. package/dist/fixtures/forge-change/judgments/personal-data/bdbcd1cdac3021c68063c3e7.yaml +18 -0
  265. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/brief.md +35 -0
  266. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/change.yaml +120 -0
  267. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/data.md +18 -0
  268. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/design.md +29 -0
  269. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/interfaces.md +17 -0
  270. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/mappings.md +26 -0
  271. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/proposal.md +35 -0
  272. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/snow.md +12 -0
  273. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/specs/transfer-limits/spec.md +19 -0
  274. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s1-refuse-over-limit.md +17 -0
  275. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s2-show-remaining.md +16 -0
  276. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s3-audit-refusals.md +18 -0
  277. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories/s4-velocity-check.md +16 -0
  278. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/stories.md +8 -0
  279. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/tasks.md +12 -0
  280. package/dist/fixtures/forge-change/openspec/changes/add-transfer-limit/ux.md +7 -0
  281. package/dist/fixtures/forge-change/org-pack/agents/code-and-test-critic.yaml +13 -0
  282. package/dist/fixtures/forge-change/org-pack/agents/complete-critic.yaml +12 -0
  283. package/dist/fixtures/forge-change/org-pack/agents/safe-critic.yaml +12 -0
  284. package/dist/fixtures/forge-change/org-pack/agents/ship-critic.yaml +11 -0
  285. package/dist/fixtures/forge-change/org-pack/agents/true-critic.yaml +11 -0
  286. package/dist/fixtures/forge-change/org-pack/checklists/review.yaml +54 -0
  287. package/dist/fixtures/forge-change/org-pack/gates.yaml +25 -0
  288. package/dist/fixtures/forge-change/org-pack/teams/team-ledger/checklists/ledger-team.yaml +227 -0
  289. package/dist/fixtures/forge-change/recordings/complete-critic/2ad84f84bf39c1d5412697e69fe23034d6983ec45a00314bf0975e627589a3c5.json +12 -0
  290. package/dist/fixtures/forge-change/recordings/complete-critic/417c98d8e54a4387d0fd93441c49f44c2868958739070d855c1dce0c41e24db3.json +12 -0
  291. package/dist/fixtures/forge-change/recordings/complete-critic/50203ee22e9cb3ef047295edb6538fe7d5695e1e4b139f3c929013dc6ce47021.json +12 -0
  292. package/dist/fixtures/forge-change/recordings/complete-critic/89f18d67f5d9c325bf35e2a9e020f9ddb6ee13b8523315037f0905cc57c040b1.json +12 -0
  293. package/dist/fixtures/forge-change/recordings/complete-critic/b4097867d26ae8eadc8b9efa8cdd86897013358035f0b40086c299a59c4ba121.json +12 -0
  294. package/dist/fixtures/forge-change/recordings/safe-critic/302d06f49664dd67ad2e21ab9cf74c1b0f7d3f750709c6ed8eff2b70983274e2.json +12 -0
  295. package/dist/fixtures/forge-change/recordings/safe-critic/32f1049c9a5ab4a759543290db4644ec6e221cc45fdfdcf06193837c6a8ce0b9.json +12 -0
  296. package/dist/fixtures/forge-change/recordings/safe-critic/4e4a84bfc04b9f26ca4250319a879901ff7a1c081c71e46a117675fee05026b0.json +12 -0
  297. package/dist/fixtures/forge-change/recordings/safe-critic/604af0fcdd6df6b76208fa88305fdc15c37606c2a4d1ae816477e46ac8c2dd04.json +12 -0
  298. package/dist/fixtures/forge-change/recordings/safe-critic/f0bf50af11f14c50d07904f0dffe1c8c20fb0cd89770645a8824c1d30a443955.json +12 -0
  299. package/dist/fixtures/forge-change/recordings/ship-critic/350954b58833b2c04880c67db45a1b328b7fd541d9f0d943b63905b8875488a6.json +12 -0
  300. package/dist/fixtures/forge-change/recordings/ship-critic/4af95c6c96a206777cec9de1f9c2d8c55a36ddc9b8db2f81d6eb83217b835efa.json +12 -0
  301. package/dist/fixtures/forge-change/recordings/ship-critic/76c34522a49cf7587a4e5689a5030d384603fa780b8aba97d27815cbc829683e.json +12 -0
  302. package/dist/fixtures/forge-change/recordings/ship-critic/a3b2cd1ace48587e5994333db03b34c3a0f9dec4f1932f1730ddfb687f6918c8.json +12 -0
  303. package/dist/fixtures/forge-change/recordings/ship-critic/de288a4fa64d0fc20890f9a787080ce6eac5203b7b9b72d884df11e8cf111835.json +12 -0
  304. package/dist/fixtures/forge-change/recordings/story-writer/1ddb00eac2aaf91a5ec5d9dfd220116be9f75aec6546a4a3a3b050ed89af2864.json +12 -0
  305. package/dist/packages/assistant-kit/frameworks-kit/README.md +86 -0
  306. package/dist/packages/assistant-kit/frameworks-kit/checklist.md +11 -0
  307. package/dist/packages/assistant-kit/frameworks-kit/examples/tide-lantern.yaml +56 -0
  308. package/dist/packages/assistant-kit/frameworks-kit/examples/tide-platform.yaml +43 -0
  309. package/dist/packages/assistant-kit/frameworks-kit/prompts/write-framework-model.md +12 -0
  310. package/dist/packages/assistant-kit/kit/README.md +98 -0
  311. package/dist/packages/assistant-kit/kit/c2-reference.md +334 -0
  312. package/dist/packages/assistant-kit/kit/checklist.md +39 -0
  313. package/dist/packages/assistant-kit/kit/examples/house-http-client.yaml +102 -0
  314. package/dist/packages/assistant-kit/kit/examples/tide-apigee.yaml +56 -0
  315. package/dist/packages/assistant-kit/kit/examples/tide-apim.yaml +32 -0
  316. package/dist/packages/assistant-kit/kit/examples/tide-kong.yaml +44 -0
  317. package/dist/packages/assistant-kit/kit/prompts/write-dialect.md +34 -0
  318. package/dist/packages/assistant-kit/kit/worked-example.md +198 -0
  319. package/dist/packages/conformance/sample/README.md +4 -0
  320. package/dist/packages/conformance/sample/inputs/notes.md +3 -0
  321. package/dist/packages/conformance/sample/inputs/systems.csv +3 -0
  322. package/dist/packages/conformance/sample/repo/api/greeter.proto +6 -0
  323. package/dist/packages/conformance/sample/repo/api/openapi.yaml +10 -0
  324. package/dist/packages/conformance/sample/repo/api/schema.graphql +3 -0
  325. package/dist/packages/conformance/sample/repo/db/V1__create_greeting.sql +4 -0
  326. package/dist/packages/conformance/sample/repo/package.json +7 -0
  327. package/dist/packages/conformance/sample/repo/src/main/java/sample/HelloController.java +10 -0
  328. package/dist/packages/conformance/sample/repo/web/app.ts +4 -0
  329. package/dist/packages/evals/datasets/critic-lenses.yaml +62 -0
  330. package/dist/packages/evals/datasets/dialect-writer.yaml +14 -0
  331. package/dist/packages/evals/datasets/feature-namer.yaml +34 -0
  332. package/dist/packages/evals/datasets/glossary-definer.yaml +25 -0
  333. package/dist/packages/evals/datasets/story-writer.yaml +13 -0
  334. package/dist/packages/evals/datasets/template-writer.yaml +16 -0
  335. package/dist/packages/evals/datasets/test-writer.yaml +18 -0
  336. package/dist/packages/executors/reference/reference-executor.mjs +233 -0
  337. package/dist/packages/extractors/dialects/argocd.yaml +45 -0
  338. package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/AccountResource.java +47 -0
  339. package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/AuditClient.java +12 -0
  340. package/dist/packages/extractors/dialects/fixtures/jaxrs/accounts-service/src/main/java/forge/accounts/LedgerClient.java +17 -0
  341. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/LedgerClient.java +14 -0
  342. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/RatesClient.java +11 -0
  343. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/java/forge/statements/StatementController.java +36 -0
  344. package/dist/packages/extractors/dialects/fixtures/micronaut/statements-service/src/main/resources/application.yml +7 -0
  345. package/dist/packages/extractors/dialects/fixtures/quarkus-reactive-messaging/fraud-service/src/main/java/forge/fraud/FraudScorer.java +27 -0
  346. package/dist/packages/extractors/dialects/fixtures/quarkus-reactive-messaging/fraud-service/src/main/resources/application.properties +4 -0
  347. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/api.tf +38 -0
  348. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/main.tf +95 -0
  349. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/terraform.tfvars +1 -0
  350. package/dist/packages/extractors/dialects/fixtures/terraform-aws/forge-aws-infra/envs/prod/variables.tf +4 -0
  351. package/dist/packages/extractors/dialects/fixtures/terraform-azure/forge-azure-infra/main.tf +102 -0
  352. package/dist/packages/extractors/dialects/fixtures/terraform-gcp/forge-gcp-infra/main.tf +51 -0
  353. package/dist/packages/extractors/dialects/harness.yaml +101 -0
  354. package/dist/packages/extractors/dialects/jaxrs.yaml +155 -0
  355. package/dist/packages/extractors/dialects/micronaut.yaml +207 -0
  356. package/dist/packages/extractors/dialects/quarkus-reactive-messaging.yaml +158 -0
  357. package/dist/packages/extractors/dialects/tekton.yaml +71 -0
  358. package/dist/packages/extractors/dialects/terraform-aws.yaml +267 -0
  359. package/dist/packages/extractors/dialects/terraform-azure.yaml +212 -0
  360. package/dist/packages/extractors/dialects/terraform-gcp.yaml +109 -0
  361. package/dist/packages/extractors/grammars/README.md +11 -0
  362. package/dist/packages/extractors/grammars/tree-sitter-groovy.LICENSE +21 -0
  363. package/dist/packages/extractors/grammars/tree-sitter-groovy.wasm +0 -0
  364. package/dist/packages/extractors/grammars/vendored.json +11 -0
  365. package/dist/packages/learn/lessons/architect-diagrams-and-code.yaml +69 -0
  366. package/dist/packages/learn/lessons/business-analyst-story-bar.yaml +94 -0
  367. package/dist/packages/learn/lessons/delivery-lead-release-readiness.yaml +44 -0
  368. package/dist/packages/learn/lessons/designer-features-in-user-words.yaml +57 -0
  369. package/dist/packages/learn/lessons/developer-find-your-way.yaml +60 -0
  370. package/dist/packages/learn/lessons/everyone-first-hour.yaml +74 -0
  371. package/dist/packages/learn/lessons/platform-lead-coverage-and-defences.yaml +58 -0
  372. package/dist/packages/learn/lessons/product-owner-first-change.yaml +167 -0
  373. package/dist/packages/learn/lessons/qa-tests-and-trace.yaml +53 -0
  374. package/dist/packages/learn/lessons/team-lead-initiative.yaml +146 -0
  375. package/dist/packages/naysayer/constitution/constitution.yaml +286 -0
  376. package/dist/packages/naysayer/starter/review.yaml +256 -0
  377. package/dist/packages/sdk/npm/index.js +8892 -0
  378. package/dist/packages/speckit/rules/drift.yaml +78 -0
  379. package/dist/packages/taskrun/rules/conform.yaml +38 -0
  380. package/dist/packages/testgen/rules/trace.yaml +49 -0
  381. package/dist/packages/ui/dist/assets/elk-api-tma0MCuh.js +2 -0
  382. package/dist/packages/ui/dist/assets/elk-worker.min--dAJroGl.js +6401 -0
  383. package/dist/packages/ui/dist/assets/elk-worker.min-DdYmS5kP.js +2 -0
  384. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
  385. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
  386. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
  387. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
  388. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
  389. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
  390. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
  391. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
  392. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
  393. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
  394. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
  395. package/dist/packages/ui/dist/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
  396. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
  397. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
  398. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
  399. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
  400. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
  401. package/dist/packages/ui/dist/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
  402. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  403. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  404. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  405. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  406. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  407. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  408. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
  409. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
  410. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
  411. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
  412. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
  413. package/dist/packages/ui/dist/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
  414. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
  415. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
  416. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
  417. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
  418. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
  419. package/dist/packages/ui/dist/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
  420. package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +29 -0
  421. package/dist/packages/ui/dist/assets/index-DD_hufQz.css +1 -0
  422. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  423. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  424. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  425. package/dist/packages/ui/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  426. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  427. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  428. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  429. package/dist/packages/ui/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  430. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  431. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  432. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  433. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  434. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  435. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  436. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  437. package/dist/packages/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  438. package/dist/packages/ui/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  439. package/dist/packages/ui/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  440. package/dist/packages/ui/dist/assets/rajdhani-devanagari-600-normal-BtY48IV_.woff +0 -0
  441. package/dist/packages/ui/dist/assets/rajdhani-devanagari-600-normal-DhS7ScYx.woff2 +0 -0
  442. package/dist/packages/ui/dist/assets/rajdhani-devanagari-700-normal-BQQOj9BB.woff2 +0 -0
  443. package/dist/packages/ui/dist/assets/rajdhani-devanagari-700-normal-DBRwWuxA.woff +0 -0
  444. package/dist/packages/ui/dist/assets/rajdhani-latin-600-normal-CXCVEoA9.woff2 +0 -0
  445. package/dist/packages/ui/dist/assets/rajdhani-latin-600-normal-Dx02qI2_.woff +0 -0
  446. package/dist/packages/ui/dist/assets/rajdhani-latin-700-normal-BPDUZo87.woff2 +0 -0
  447. package/dist/packages/ui/dist/assets/rajdhani-latin-700-normal-e0Uj8ZlN.woff +0 -0
  448. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-600-normal-BleIgkPp.woff +0 -0
  449. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-600-normal-C9ZjJQAt.woff2 +0 -0
  450. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-700-normal-BPaQ7tlD.woff +0 -0
  451. package/dist/packages/ui/dist/assets/rajdhani-latin-ext-700-normal-S6aYESQK.woff2 +0 -0
  452. package/dist/packages/ui/dist/favicon.svg +1 -0
  453. package/dist/packages/ui/dist/index.html +15 -0
  454. package/dist/third-party.json +575 -0
  455. package/dist/vendor/@fission-ai/openspec/bin/openspec.js +85986 -0
  456. package/dist/vendor/@fission-ai/openspec/package.json +82 -0
  457. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/schema.yaml +248 -0
  458. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/design.md +21 -0
  459. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/proposal.md +34 -0
  460. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/spec.md +13 -0
  461. package/dist/vendor/@fission-ai/openspec/schemas/spec-driven/templates/tasks.md +11 -0
  462. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
  463. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
  464. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
  465. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
  466. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
  467. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
  468. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
  469. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
  470. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
  471. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
  472. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
  473. package/dist/vendor/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
  474. package/dist/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
  475. package/package.json +36 -3
@@ -0,0 +1,1496 @@
1
+ import { createRequire as __atticusCreateRequire } from "node:module";
2
+ import { fileURLToPath as __atticusFileURLToPath } from "node:url";
3
+ var require = __atticusCreateRequire(import.meta.url);
4
+ var __atticusUrl = (p) => new URL("./" + p, import.meta.url).href;
5
+ var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
6
+ import {
7
+ BEGIN_RE,
8
+ END,
9
+ buildEngineFor,
10
+ findSection,
11
+ generateAssistantFiles,
12
+ parseHeader,
13
+ writeAssistantFiles
14
+ } from "./chunk-V5PKHHQQ.js";
15
+ import {
16
+ CONSTITUTION_FILE_LABEL,
17
+ buildContext,
18
+ docOf,
19
+ findingIdFor,
20
+ fingerprint,
21
+ layerRules,
22
+ lensAgent,
23
+ loadConstitution,
24
+ parseDoc,
25
+ parseLensOutput,
26
+ questionOfRule,
27
+ readChangeFiles,
28
+ readDataChanges,
29
+ readHops,
30
+ readPackFiles,
31
+ readRound,
32
+ roundsOf,
33
+ sectionMatching,
34
+ toFinding,
35
+ writeFinding
36
+ } from "./chunk-7UFQZXZA.js";
37
+ import {
38
+ changeFolder,
39
+ changeFootprint,
40
+ changeStore,
41
+ changeTypeOf,
42
+ loadChangeContext,
43
+ referenceMetarepoIn
44
+ } from "./chunk-56YQ6ROQ.js";
45
+ import {
46
+ readSpecKitFeatures
47
+ } from "./chunk-MC3H7736.js";
48
+ import {
49
+ baseClonePath,
50
+ changeWorktreesDir,
51
+ createGit,
52
+ loadRepos,
53
+ readChangeRepos,
54
+ toRepoRelative
55
+ } from "./chunk-KJPMEAI4.js";
56
+ import {
57
+ fsRepo
58
+ } from "./chunk-GQ4N4WKF.js";
59
+ import {
60
+ buildContextPack,
61
+ toRunSummary
62
+ } from "./chunk-CNUKCS2T.js";
63
+ import {
64
+ ChecklistPack,
65
+ Finding,
66
+ RecordId,
67
+ changeBranch,
68
+ changesRelOf,
69
+ normalizePath,
70
+ orgPackDirOf,
71
+ require_dist,
72
+ taskBranch,
73
+ toCanonicalYaml
74
+ } from "./chunk-S2A7YC3Q.js";
75
+ import {
76
+ writeFileAtomic
77
+ } from "./chunk-D3PO4DOZ.js";
78
+ import {
79
+ __toESM
80
+ } from "./chunk-TV45EUGM.js";
81
+
82
+ // packages/speckit/dist/change.js
83
+ import { readFile } from "node:fs/promises";
84
+ import { join } from "node:path";
85
+ var SpecKitError = class extends Error {
86
+ code;
87
+ constructor(code, message) {
88
+ super(message);
89
+ this.code = code;
90
+ this.name = "SpecKitError";
91
+ }
92
+ };
93
+ var TASK_LINE = /^(\s*[-*]\s+\[)([ xX]?)(\]\s+)(\S+)\s+(.*)$/;
94
+ function readChangeTasks(files) {
95
+ const doc = docOf(files, "tasks.md");
96
+ if (!doc)
97
+ return [];
98
+ const out = [];
99
+ let repo = "";
100
+ doc.lines.forEach((l, i) => {
101
+ const h = /^##\s+(.+?)\s*$/.exec(l);
102
+ if (h) {
103
+ repo = h[1].replace(/[`*]/g, "").trim().split(/\s+/)[0].toLowerCase();
104
+ return;
105
+ }
106
+ const m = TASK_LINE.exec(l);
107
+ if (!m || !repo || !RecordId.safeParse(m[4].toLowerCase()).success)
108
+ return;
109
+ const rest = m[5];
110
+ const stories = [...rest.matchAll(/\bstor(?:y|ies)\s+([a-z0-9][a-z0-9._-]*)/gi)].map((s) => s[1].toLowerCase().replace(/[.,)]+$/, ""));
111
+ out.push({
112
+ id: m[4].toLowerCase(),
113
+ repo,
114
+ title: rest.replace(/\s*\((?:story|stories)\b[^)]*\)\s*$/i, "").trim(),
115
+ stories,
116
+ line: i + 1,
117
+ done: m[2].toLowerCase() === "x"
118
+ });
119
+ });
120
+ return out;
121
+ }
122
+ async function markTaskDone(root, changeId, task) {
123
+ const path = join(changeFolder(root, changeId), "tasks.md");
124
+ const lines = (await readFile(path, "utf8")).split("\n");
125
+ const l = lines[task.line - 1];
126
+ const m = l === void 0 ? null : TASK_LINE.exec(l.replace(/\r$/, ""));
127
+ if (!m || m[4].toLowerCase() !== task.id)
128
+ return false;
129
+ if (m[2].toLowerCase() === "x")
130
+ return false;
131
+ lines[task.line - 1] = l.replace(TASK_LINE, (_a, a, _b, c, d, e) => `${a}x${c}${d} ${e}`);
132
+ await writeFileAtomic(path, lines.join("\n"));
133
+ return true;
134
+ }
135
+ function readStory(files, id) {
136
+ const doc = files.docs.get(`stories/${id}.md`);
137
+ if (!doc)
138
+ return void 0;
139
+ const title = doc.sections.find((s) => s.level === 1)?.heading ?? id;
140
+ const first = doc.sections.find((s) => s.level === 2);
141
+ const start = (doc.sections.find((s) => s.level === 1)?.line ?? 0) + 1;
142
+ const end = first ? first.line - 1 : doc.lines.length;
143
+ const text = doc.lines.slice(start - 1, end).join("\n").trim().split("\n");
144
+ const ac = doc.sections.find((s) => /acceptance criteria/i.test(s.heading));
145
+ const criteria = ac ? doc.lines.slice(ac.line, ac.end).map((l) => /^\s*[-*]\s+(.*)$/.exec(l)?.[1]?.trim()).filter((x) => !!x) : [];
146
+ return { id, title, text: text[0] === "" ? [] : text, criteria, path: doc.path };
147
+ }
148
+ function readTaskKnowledge(files, storyIds, task) {
149
+ const out = [];
150
+ for (const id of storyIds) {
151
+ const doc = files.docs.get(`stories/${id}.md`);
152
+ if (!doc)
153
+ continue;
154
+ const head = new RegExp(`^###\\s+Task\\s+${task.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:`, "i");
155
+ const lines = doc.rawLines;
156
+ const at = lines.findIndex((l) => head.test(l));
157
+ if (at < 0)
158
+ continue;
159
+ let k = lines.findIndex((l, j) => j > at && /^- Knowledge that applies:\s*$/.test(l));
160
+ const next = lines.findIndex((l, j) => j > at && /^#{1,3}\s/.test(l));
161
+ if (k < 0 || next >= 0 && k > next)
162
+ continue;
163
+ for (k += 1; k < lines.length && /^\s{2,}\S/.test(lines[k]); k++)
164
+ out.push(lines[k].slice(2));
165
+ }
166
+ return out;
167
+ }
168
+ var STATES_WITH_APPROVED_DESIGN = /* @__PURE__ */ new Set([
169
+ "plan",
170
+ "build",
171
+ "qa",
172
+ "release-ready",
173
+ "released",
174
+ "post-release-review"
175
+ ]);
176
+ async function loadTask(root, changeId, taskId, o = {}) {
177
+ const ctx = await loadChangeContext(root);
178
+ const change = await changeStore(root).read(changeId).catch((e) => {
179
+ throw new SpecKitError("not-found", e.message);
180
+ });
181
+ if (o.needApprovedDesign !== false && !STATES_WITH_APPROVED_DESIGN.has(change.state))
182
+ throw new SpecKitError("refused", `${change.id} is ${change.state}: a task is handed down only after the design is approved (plan phase or later).`);
183
+ const files = await readChangeFiles(changeFolder(root, change.id));
184
+ const tasks = readChangeTasks(files);
185
+ const task = tasks.find((t) => t.id === taskId.toLowerCase());
186
+ if (!task)
187
+ throw new SpecKitError("not-found", tasks.length ? `There is no task ${taskId} in ${change.id}'s tasks.md. Tasks: ${tasks.map((t) => t.id).join(", ")}.` : `${change.id} has no tasks yet: tasks.md needs \`- [ ] <id> <what>\` lines under a \`## <repo>\` heading.`);
188
+ const repo = (await loadRepos(root)).find((r) => r.id === task.repo);
189
+ if (!repo)
190
+ throw new SpecKitError("not-found", `Task ${task.id} is under ${task.repo}, which no repos.manifest.yaml lists.`);
191
+ return {
192
+ root,
193
+ ctx,
194
+ change,
195
+ files,
196
+ task,
197
+ repo,
198
+ component: repo.component ?? repo.id,
199
+ hops: readHops(files),
200
+ data: readDataChanges(files),
201
+ patterns: new Map(ctx.orgPack.patterns.map((p) => [p.code, p]))
202
+ };
203
+ }
204
+ async function changeWorktree(root, changeId, repo) {
205
+ const r = (await readChangeRepos(root, changeId))?.repos.find((x) => x.id === repo);
206
+ if (!r)
207
+ throw new SpecKitError("refused", `${repo} has no worktree for ${changeId} yet. Open it first: atticus workspace open ${changeId} --repos ${repo}`);
208
+ return join(root, r.worktree);
209
+ }
210
+ function viaOf(patterns, code) {
211
+ const route = patterns.get(code)?.route;
212
+ if (!route)
213
+ return void 0;
214
+ return ["gateway", "direct", "event-bus", "pubsub", "sftp", "mq"].includes(route) ? route : "other";
215
+ }
216
+ function mappingSections(doc) {
217
+ if (!doc)
218
+ return [];
219
+ return doc.sections.filter((s) => s.level === 2).map((s) => {
220
+ const m = /^([A-Za-z]+)\s+(\S+)$/.exec(s.heading.trim());
221
+ if (!m)
222
+ return void 0;
223
+ const body = doc.rawLines.slice(s.line, s.end).join("\n").trim();
224
+ return { method: m[1].toUpperCase(), path: m[2], lines: body ? body.split("\n") : [] };
225
+ }).filter((x) => !!x);
226
+ }
227
+
228
+ // packages/speckit/dist/handdown.js
229
+ import { mkdir, readFile as readFile2, rename, writeFile } from "node:fs/promises";
230
+ import { dirname, join as join3 } from "node:path";
231
+
232
+ // packages/speckit/dist/constitution.js
233
+ import { join as join2 } from "node:path";
234
+ var ROMAN = [
235
+ [1e3, "M"],
236
+ [900, "CM"],
237
+ [500, "D"],
238
+ [400, "CD"],
239
+ [100, "C"],
240
+ [90, "XC"],
241
+ [50, "L"],
242
+ [40, "XL"],
243
+ [10, "X"],
244
+ [9, "IX"],
245
+ [5, "V"],
246
+ [4, "IV"],
247
+ [1, "I"]
248
+ ];
249
+ function roman(n) {
250
+ let out = "";
251
+ for (const [v, s] of ROMAN)
252
+ while (n >= v) {
253
+ out += s;
254
+ n -= v;
255
+ }
256
+ return out;
257
+ }
258
+ async function rulesForRepo(root, team) {
259
+ const constitution = await loadConstitution();
260
+ const sources = [
261
+ { pack: constitution, file: CONSTITUTION_FILE_LABEL, origin: "constitution" }
262
+ ];
263
+ const org = await readPackFiles(root, join2(orgPackDirOf(root), "checklists"));
264
+ for (const p of org.packs)
265
+ sources.push({ ...p, origin: "org" });
266
+ const problems = [...org.problems];
267
+ if (team) {
268
+ const t = await readPackFiles(root, join2(orgPackDirOf(root), "teams", team, "checklists"));
269
+ problems.push(...t.problems);
270
+ for (const p of t.packs)
271
+ sources.push({ ...p, origin: "team" });
272
+ }
273
+ const layered = layerRules(sources);
274
+ problems.push(...layered.problems, ...layered.refused.map((r) => `${r.file}: ${r.reason}`));
275
+ return {
276
+ rules: layered.rules.filter((r) => r.status === "active"),
277
+ problems,
278
+ constitutionVersion: constitution.version
279
+ };
280
+ }
281
+ var describe = (r) => (r.description ?? (r.check.type === "ai" ? r.check.prompt : r.title)).trim();
282
+ var line = (r) => `Rule \`${r.id}\` (${r.lens} lens, ${r.severity}${r.effectiveLayer === "constitution" || !r.waivable ? ", never waived" : ", waivable by the stage owner"}).`;
283
+ function ruleSections(o, depth) {
284
+ const byLayer = (l) => o.rules.filter((r) => r.effectiveLayer === l);
285
+ const core = byLayer("constitution");
286
+ const org = byLayer("org");
287
+ const team = byLayer("team");
288
+ const h2 = "#".repeat(depth + 1);
289
+ const h3 = "#".repeat(depth + 2);
290
+ const block = (r, heading) => [heading, "", describe(r), "", line(r), ""];
291
+ return [
292
+ `${h2} Core Principles`,
293
+ "",
294
+ ...core.flatMap((r, i) => block(r, `${h3} ${roman(i + 1)}. ${r.title}`)),
295
+ ...org.length ? [`${h2} Organisation Rules`, "", ...org.flatMap((r) => block(r, `${h3} ${r.title}`))] : [],
296
+ ...team.length ? [
297
+ `${h2} Team Rules (${o.team ?? "owning team"})`,
298
+ "",
299
+ ...team.flatMap((r) => block(r, `${h3} ${r.title}`))
300
+ ] : []
301
+ ];
302
+ }
303
+ var packName = (o) => o.orgPack.pack ? `${o.orgPack.pack.name} ${o.orgPack.pack.version}` : "built-in defaults";
304
+ function renderConstitutionSection(o) {
305
+ return [
306
+ "## Atticus Rules",
307
+ "",
308
+ `Merged by Atticus from the Atticus constitution ${o.constitutionVersion} and the org pack (${packName(o)}): the rules the critic applies at every gate, which apply here alongside the team's own principles. Everything outside this section is the team's own and Atticus never changes it. Change these rules in the org pack, never here; hand-down regenerates this section.`,
309
+ "",
310
+ ...ruleSections(o, 2)
311
+ ].join("\n").replace(/\n+$/, "\n");
312
+ }
313
+ function constitutionPlan(current) {
314
+ if (current === void 0 || parseHeader(current))
315
+ return { mode: "file" };
316
+ const lines = current.replace(/\r\n/g, "\n").split("\n");
317
+ const begins = lines.filter((l) => /^<!-- atticus:begin\b/.test(l)).length;
318
+ const ends = lines.filter((l) => l === END).length;
319
+ if (begins === 0 && ends === 0)
320
+ return { mode: "section" };
321
+ if (begins === 1 && ends === 1 && findSection(current))
322
+ return { mode: "section" };
323
+ const why = begins > ends ? "a begin marker without an end marker" : begins < ends ? "an end marker without a begin marker" : begins > 1 ? "more than one Atticus section" : !lines.some((l) => BEGIN_RE.test(l)) ? "a begin marker Atticus can't read" : "an end marker before the begin marker";
324
+ return {
325
+ mode: "refuse",
326
+ reason: `.specify/memory/constitution.md is the team's own constitution and its Atticus section markers are broken (${why}), so Atticus can't merge its rules into it. Fix or remove the markers, then hand down again.`
327
+ };
328
+ }
329
+ function renderConstitution(o) {
330
+ const pack = packName(o);
331
+ return [
332
+ `# ${o.repo} Constitution`,
333
+ "",
334
+ `Generated from the Atticus constitution ${o.constitutionVersion} and the org pack (${pack}): the rules the critic applies at every gate. Change them in the org pack, never here.`,
335
+ "",
336
+ ...ruleSections(o, 1),
337
+ "## Governance",
338
+ "",
339
+ "This constitution is generated by Atticus from the org pack, the same source as the critic's constitution layer. Core principles are never waived, and a team can only tighten a rule, never drop or loosen it. Amend it with a pull request to the org pack; hand-down regenerates this file.",
340
+ "",
341
+ `**Version**: ${o.constitutionVersion}+${(o.orgPack.pack?.name ?? "defaults").replace(/[^a-z0-9.-]/g, "-")}.${o.orgPack.pack?.version ?? "0.0.0"} | **Source**: org-pack and the Atticus constitution`,
342
+ ""
343
+ ].join("\n");
344
+ }
345
+
346
+ // packages/speckit/dist/render.js
347
+ var import_yaml = __toESM(require_dist(), 1);
348
+ var NC = "NEEDS CLARIFICATION";
349
+ var date = (t) => t.change.createdAt.slice(0, 10);
350
+ var GWT = /^given\s+(.+?),\s*when\s+(.+?),\s*then\s+(.+)$/i;
351
+ function renderSpec(t) {
352
+ const stories = t.task.stories.map((s) => readStory(t.files, s)).filter((s) => !!s);
353
+ const out = [
354
+ `# Feature Specification: ${t.task.title}`,
355
+ "",
356
+ `**Feature Branch**: \`${taskBranch(t.change.id, t.task.id)}\``,
357
+ `**Created**: ${date(t)}`,
358
+ "**Status**: Draft",
359
+ `**Input**: Task ${t.task.id} of change ${t.change.id} (${t.change.title})`,
360
+ "",
361
+ `<!-- Handed down by Atticus from ${changesRelOf(t.root)}/${t.change.id}: ${stories.length ? stories.map((s) => s.path).join(", ") : "tasks.md"}. Refine it with /speckit.specify and /speckit.clarify. A question that needs the change's design goes back up, never into this repo alone. -->`,
362
+ "",
363
+ "## User Scenarios & Testing *(mandatory)*",
364
+ ""
365
+ ];
366
+ const frs = [];
367
+ stories.forEach((s, i) => {
368
+ out.push(`### User Story ${i + 1} - ${s.title} (Priority: P${i + 1})`, "");
369
+ if (s.text.length)
370
+ out.push(...s.text, "");
371
+ out.push("**Acceptance Scenarios**:", "");
372
+ if (s.criteria.length === 0)
373
+ out.push(`1. [${NC}: the story has no acceptance criteria]`);
374
+ s.criteria.forEach((c, j) => {
375
+ const m = GWT.exec(c);
376
+ out.push(`${j + 1}. ${m ? `**Given** ${m[1]}, **When** ${m[2]}, **Then** ${m[3]}` : c}`);
377
+ frs.push(`System MUST meet acceptance scenario ${j + 1} of User Story ${i + 1}.`);
378
+ });
379
+ out.push("");
380
+ });
381
+ if (stories.length === 0)
382
+ out.push(`### User Story 1 - ${t.task.title} (Priority: P1)`, "", `[${NC}: the task names no story]`, "");
383
+ out.push("### Edge Cases", "", `- [${NC}: cases the story's acceptance criteria don't cover]`, "", "## Requirements *(mandatory)*", "", "### Functional Requirements", "", ...frs.length ? frs.map((f, i) => `- **FR-${String(i + 1).padStart(3, "0")}**: ${f}`) : [`- **FR-001**: [${NC}: no acceptance criteria to trace]`], "", "## Success Criteria *(mandatory)*", "", "### Measurable Outcomes", "", "- **SC-001**: Every acceptance scenario above passes as an automated test.", "");
384
+ return out.join("\n");
385
+ }
386
+ var isHttp = (h) => !!h.method && ["GET", "PUT", "POST", "DELETE", "PATCH", "HEAD", "OPTIONS"].includes(h.method);
387
+ var topicOf = (operation) => /^(publish|subscribe|send|receive)\s+(\S+)/i.exec(operation.trim());
388
+ function repoHops(t) {
389
+ return {
390
+ calls: t.hops.filter((h) => h.caller === t.component),
391
+ serves: t.hops.filter((h) => h.callee === t.component && isHttp(h))
392
+ };
393
+ }
394
+ function repoData(t) {
395
+ const owned = new Set(t.ctx.records.filter((r) => r.kind === "DataStore" && r.ownerComponent === t.component).map((r) => r.id));
396
+ const tableOwner = (id) => t.ctx.records.find((r) => r.kind === "Table" && r.id === id)?.ownerComponent;
397
+ return t.data.filter((d) => owned.has(d.store) || tableOwner(d.tableId) === t.component);
398
+ }
399
+ function contractFile(t) {
400
+ return `contracts/${t.component}.yaml`;
401
+ }
402
+ function renderPlan(t) {
403
+ const { calls, serves } = repoHops(t);
404
+ const data = repoData(t);
405
+ const stores = [...new Set(data.map((d) => d.store))];
406
+ const owned = t.ctx.records.filter((r) => r.kind === "DataStore" && r.ownerComponent === t.component);
407
+ const storage = [.../* @__PURE__ */ new Set([...owned.map((s) => s.id), ...stores])].sort().map((id) => {
408
+ const s = owned.find((x) => x.id === id);
409
+ return s?.storeType ? `${id} (${s.storeType})` : id;
410
+ });
411
+ const platforms = [
412
+ ...new Set(t.ctx.records.filter((r) => r.kind === "Deployment" && r.component === t.component).map((r) => r.platform))
413
+ ].sort();
414
+ const design = docOf(t.files, "design.md");
415
+ const approach = sectionMatching(design, /^approach$/i);
416
+ const summary = design && approach ? design.rawLines.slice(approach.line, approach.end).join("\n").trim() : `[${NC}: the change's design.md has no Approach section]`;
417
+ const status = (s) => /^planned-(new|modify|retire)$/.test(s) ? s : "planned-new";
418
+ const mapping = mappingSections(docOf(t.files, "mappings.md")).filter((m) => [...calls, ...serves].some((h) => h.method === m.method && h.path === m.path));
419
+ const feature = `specs/${t.task.id}`;
420
+ const out = [
421
+ `# Implementation Plan: ${t.task.title}`,
422
+ "",
423
+ `**Branch**: \`${taskBranch(t.change.id, t.task.id)}\` | **Date**: ${date(t)} | **Spec**: [spec.md](./spec.md)`,
424
+ `**Input**: Feature specification from \`${feature}/spec.md\``,
425
+ "",
426
+ `<!-- Seeded by Atticus from the approved design of change ${t.change.id} for ${t.repo.id} (component ${t.component}). The services table, data-model.md and contracts/ are the design: Atticus reads them on every scan and reports drift, so amend the design through the change instead of here. -->`,
427
+ "",
428
+ "## Summary",
429
+ "",
430
+ summary,
431
+ "",
432
+ "## Technical Context",
433
+ "",
434
+ `**Language/Version**: ${NC}`,
435
+ `**Primary Dependencies**: ${NC}`,
436
+ `**Storage**: ${storage.length ? storage.join(", ") : "N/A"}`,
437
+ `**Testing**: ${NC}`,
438
+ `**Target Platform**: ${platforms.length ? platforms.join(", ") : NC}`,
439
+ "**Project Type**: single",
440
+ `**Performance Goals**: ${NC}`,
441
+ `**Constraints**: ${NC}`,
442
+ "",
443
+ "### Services this feature calls",
444
+ "",
445
+ "| Service | Operation | Via | Pattern | Status |",
446
+ "| --- | --- | --- | --- | --- |",
447
+ ...calls.map((h) => {
448
+ const via = viaOf(t.patterns, h.pattern) ?? (topicOf(h.operation) ? "event-bus" : "other");
449
+ return `| ${h.callee} | ${h.operation.trim()} | ${via} | ${h.pattern} | ${status(h.status)} |`;
450
+ }),
451
+ ""
452
+ ];
453
+ if (serves.length)
454
+ out.push("### Operations this feature serves", "", "| Operation | Caller | Pattern | Status | Contract |", "| --- | --- | --- | --- | --- |", ...serves.map((h) => `| ${h.method} ${h.path} | ${h.caller} | ${h.pattern} | ${status(h.status)} | ${contractFile(t)} |`), "");
455
+ if (mapping.length)
456
+ out.push("### Field mappings", "", ...mapping.flatMap((m) => [`#### ${m.method} ${m.path}`, "", ...m.lines, ""]));
457
+ const knowledge = readTaskKnowledge(t.files, t.task.stories, t.task.id);
458
+ if (knowledge.length)
459
+ out.push("### Knowledge that applies", "", `<!-- From the task's story in change ${t.change.id}: the organisation's pages (wikis and documents) that apply to this task, quoted as data. Where pages disagree, a person or the code decides; ask rather than pick. -->`, "", ...knowledge, "");
460
+ out.push("## Constitution Check", "", `[${NC}: check each principle of \`.specify/memory/constitution.md\`, generated from the org pack]`, "", "## Project Structure", "", "### Documentation (this feature)", "", "```text", `${feature}/`, "\u251C\u2500\u2500 plan.md", ...data.length ? ["\u251C\u2500\u2500 data-model.md"] : [], ...serves.length || calls.some((h) => topicOf(h.operation)) ? ["\u251C\u2500\u2500 contracts/"] : [], "\u2514\u2500\u2500 tasks.md", "```", "", "### Source Code (repository root)", "", "```text", `[${NC}: the files this feature adds or changes]`, "```", "", "## Open questions", "", "- None yet. Anything that needs the change's design goes here; it goes back up as an owner question.", "");
461
+ return out.join("\n");
462
+ }
463
+ function renderDataModel(t) {
464
+ const data = repoData(t);
465
+ if (data.length === 0)
466
+ return void 0;
467
+ const stores = [...new Set(data.map((d) => d.store))].sort();
468
+ const tables = /* @__PURE__ */ new Map();
469
+ for (const d of data) {
470
+ const name = d.table.includes(".") ? d.table.split(".").pop() : d.table;
471
+ const e = tables.get(d.tableId) ?? { name, store: d.store, isNew: false, rows: [] };
472
+ e.isNew ||= d.newTable;
473
+ for (const c of d.newColumns) {
474
+ let type = c.type;
475
+ const notNull = /\bnot null\b/i.test(type) || !d.newTable && /\bnot null\b/i.test(d.change);
476
+ type = type.replace(/\s*\bnot null\b.*$/i, "").replace(/\s*\bdefault\b.*$/i, "").trim();
477
+ const notes = d.newTable ? "" : `${d.change.trim()}${d.migration ? ` (${d.migration})` : ""}`;
478
+ e.rows.push(`| ${c.name} | ${type} | ${notNull ? "no" : ""} | ${notes} |`);
479
+ }
480
+ tables.set(d.tableId, e);
481
+ }
482
+ const out = [
483
+ `# Data Model: ${t.task.title}`,
484
+ "",
485
+ ...stores.length === 1 ? [`**Data store**: ${stores[0]}`, ""] : [],
486
+ `<!-- Seeded by Atticus from data.md of change ${t.change.id}. Keep the headings and tables in this shape: Atticus reads them on every scan. -->`,
487
+ "",
488
+ "## Entities",
489
+ ""
490
+ ];
491
+ for (const e of tables.values()) {
492
+ out.push(`### ${e.name} (${e.isNew ? "new" : "existing"} table)`, "");
493
+ if (stores.length > 1)
494
+ out.push(`**Store**: ${e.store}`, "");
495
+ out.push("| Column | Type | Nullable | Notes |", "| --- | --- | --- | --- |", ...e.rows, "");
496
+ }
497
+ return out.join("\n").replace(/\n+$/, "\n");
498
+ }
499
+ function renderOpenApi(t) {
500
+ const { serves } = repoHops(t);
501
+ if (serves.length === 0)
502
+ return void 0;
503
+ const paths = {};
504
+ for (const h of serves) {
505
+ const params = [...h.path.matchAll(/\{([^}]+)\}/g)].map((m) => ({
506
+ name: m[1],
507
+ in: "path",
508
+ required: true,
509
+ schema: { type: "string" }
510
+ }));
511
+ const op = {
512
+ operationId: `${h.method.toLowerCase()}-${h.path.replace(/[{}]/g, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase()}`,
513
+ summary: `Called by ${h.caller}${h.pattern ? ` (${h.pattern})` : ""}`,
514
+ "x-atticus-status": /^planned-(new|modify|retire)$/.test(h.status) ? h.status : "planned-new",
515
+ ...h.pattern ? { "x-atticus-pattern": h.pattern } : {},
516
+ ...params.length ? { parameters: params } : {},
517
+ responses: { default: { description: "See the change's mappings.md" } }
518
+ };
519
+ (paths[h.path] ??= {})[h.method.toLowerCase()] = op;
520
+ }
521
+ return (0, import_yaml.stringify)({
522
+ openapi: "3.0.3",
523
+ info: {
524
+ title: `${t.component}, as planned by change ${t.change.id}`,
525
+ version: "0.0.0",
526
+ description: "Seeded by Atticus from interfaces.md. Fill in schemas while planning; keep the paths and methods, or amend the design."
527
+ },
528
+ paths
529
+ }, { lineWidth: 0 });
530
+ }
531
+ function renderAsyncApi(t) {
532
+ const topics = repoHops(t).calls.map((h) => ({ h, m: topicOf(h.operation) })).filter((x) => !!x.m);
533
+ if (topics.length === 0)
534
+ return void 0;
535
+ const channels = {};
536
+ const operations = {};
537
+ for (const { h, m } of topics) {
538
+ const key = m[2].replace(/[^A-Za-z0-9]+/g, "-");
539
+ channels[key] = { address: m[2] };
540
+ const send = /^(publish|send)$/i.test(m[1]);
541
+ operations[`${send ? "send" : "receive"}-${key}`] = {
542
+ action: send ? "send" : "receive",
543
+ channel: { $ref: `#/channels/${key}` },
544
+ summary: `${send ? "Published for" : "Read from"} ${h.callee}${h.pattern ? ` (${h.pattern})` : ""}`
545
+ };
546
+ }
547
+ return (0, import_yaml.stringify)({
548
+ asyncapi: "3.0.0",
549
+ info: {
550
+ title: `${t.component} events, as planned by change ${t.change.id}`,
551
+ version: "0.0.0"
552
+ },
553
+ channels,
554
+ operations
555
+ }, { lineWidth: 0 });
556
+ }
557
+ function renderChecklist(t) {
558
+ const dor = t.ctx.orgPack.testStrategy.definitionOfReady;
559
+ return [
560
+ `# Specification Quality Checklist: ${t.task.title}`,
561
+ "",
562
+ "**Purpose**: Check spec.md is ready before planning, against the org pack's definition of ready",
563
+ "**Feature**: [spec.md](../spec.md)",
564
+ "",
565
+ "## Definition of ready",
566
+ "",
567
+ ...dor.length ? dor.map((d, i) => `- [ ] CHK${String(i + 1).padStart(3, "0")} ${d}`) : ["- [ ] CHK001 Every acceptance criterion is testable"],
568
+ ""
569
+ ].join("\n");
570
+ }
571
+ function renderTasks(t) {
572
+ const stories = t.task.stories.map((s) => readStory(t.files, s)).filter((s) => !!s);
573
+ const lines = [
574
+ `# Tasks: ${t.task.title}`,
575
+ "",
576
+ `**Input**: spec.md and plan.md in this folder (task ${t.task.id} of change ${t.change.id})`,
577
+ "",
578
+ "<!-- Seeded by Atticus at hand-down. Refine it with /speckit.tasks; tick each box as you finish it. -->",
579
+ "",
580
+ "## Implementation",
581
+ "",
582
+ `- [ ] T001 ${t.task.title}`,
583
+ "",
584
+ "## Tests",
585
+ ""
586
+ ];
587
+ let n = 2;
588
+ for (const s of stories)
589
+ for (const c of s.criteria)
590
+ lines.push(`- [ ] T${String(n++).padStart(3, "0")} [${s.id}] Test: ${c.replace(/\s+/g, " ").trim()}`);
591
+ if (n === 2)
592
+ lines.push(`- [ ] T002 Test the behaviour spec.md describes [${NC}: no acceptance criteria]`);
593
+ lines.push("");
594
+ return lines.join("\n");
595
+ }
596
+ function renderAtticusTask(t) {
597
+ const spec = renderSpec(t).split("\n");
598
+ const plan = renderPlan(t).split("\n");
599
+ const from = (lines, heading) => {
600
+ const i = lines.indexOf(heading);
601
+ if (i < 0)
602
+ return [];
603
+ const end = lines.findIndex((l, j) => j > i && /^## /.test(l));
604
+ return lines.slice(i + 1, end < 0 ? void 0 : end);
605
+ };
606
+ return [
607
+ `# Task ${t.task.id}: ${t.task.title}`,
608
+ "",
609
+ `Change ${t.change.id} (${t.change.title}), branch \`${taskBranch(t.change.id, t.task.id)}\`. Handed down by Atticus; amend the design through the change, not here.`,
610
+ "",
611
+ "## Stories and acceptance criteria",
612
+ ...from(spec, "## User Scenarios & Testing *(mandatory)*"),
613
+ "## Design for this repo",
614
+ ...from(plan, "## Summary"),
615
+ ...from(plan, "## Technical Context")
616
+ ].join("\n").replace(/\n{3,}/g, "\n\n").replace(/\n*$/, "\n");
617
+ }
618
+
619
+ // packages/speckit/dist/handdown.js
620
+ async function putIfFree(dir, rel, text, force, r) {
621
+ const full = join3(dir, rel);
622
+ const current = await readFile2(full, "utf8").catch(() => void 0);
623
+ if (current === text) {
624
+ r.unchanged.push(rel);
625
+ return;
626
+ }
627
+ if (current !== void 0 && !force) {
628
+ r.kept.push(rel);
629
+ return;
630
+ }
631
+ await mkdir(dirname(full), { recursive: true });
632
+ await writeFile(`${full}.atticus-tmp`, text, "utf8");
633
+ await rename(`${full}.atticus-tmp`, full);
634
+ r.written.push(rel);
635
+ }
636
+ function handDownFiles(t, engine) {
637
+ const files = /* @__PURE__ */ new Map();
638
+ if (engine === "atticus") {
639
+ files.set(`.atticus/tasks/${t.task.id}.md`, renderAtticusTask(t));
640
+ return files;
641
+ }
642
+ const dir = `specs/${t.task.id}`;
643
+ files.set(`${dir}/spec.md`, renderSpec(t));
644
+ files.set(`${dir}/plan.md`, renderPlan(t));
645
+ files.set(`${dir}/tasks.md`, renderTasks(t));
646
+ const dm = renderDataModel(t);
647
+ if (dm)
648
+ files.set(`${dir}/data-model.md`, dm);
649
+ const oa = renderOpenApi(t);
650
+ if (oa)
651
+ files.set(`${dir}/${contractFile(t)}`, oa);
652
+ const aa = renderAsyncApi(t);
653
+ if (aa)
654
+ files.set(`${dir}/contracts/${t.component}-events.yaml`, aa);
655
+ files.set(`${dir}/checklists/requirements.md`, renderChecklist(t));
656
+ return files;
657
+ }
658
+ function featureJson(t) {
659
+ return JSON.stringify({
660
+ feature_directory: `specs/${t.task.id}`,
661
+ change: t.change.id,
662
+ task: t.task.id,
663
+ branch: taskBranch(t.change.id, t.task.id)
664
+ }, null, 2) + "\n";
665
+ }
666
+ async function handDown(root, changeId, taskId, o = {}) {
667
+ const t = await loadTask(root, changeId, taskId);
668
+ const dir = await changeWorktree(root, t.change.id, t.repo.id);
669
+ const engine = buildEngineFor(t.ctx.orgPack, t.repo.id);
670
+ const r = {
671
+ change: t.change.id,
672
+ task: t.task.id,
673
+ repo: t.repo.id,
674
+ engine,
675
+ worktree: toRepoRelative(root, dir),
676
+ feature: engine === "speckit" ? `specs/${t.task.id}` : ".atticus/tasks",
677
+ written: [],
678
+ unchanged: [],
679
+ kept: [],
680
+ assistantFiles: { results: [], upToDate: true },
681
+ problems: []
682
+ };
683
+ for (const [rel, text] of handDownFiles(t, engine))
684
+ await putIfFree(dir, rel, text, !!o.force, r);
685
+ const ref = await referenceMetarepoIn(root, dir, o.git ? { git: o.git } : {});
686
+ if (ref.action === "added")
687
+ r.written.push(ref.file);
688
+ else if (ref.action === "present")
689
+ r.unchanged.push(ref.file);
690
+ else if (ref.action === "skipped")
691
+ r.problems.push(`OpenSpec reference not added to ${ref.file}: ${ref.why}`);
692
+ if (engine === "speckit") {
693
+ await putIfFree(dir, ".specify/feature.json", featureJson(t), true, r);
694
+ const rules = await rulesForRepo(root, t.repo.owner);
695
+ r.problems.push(...rules.problems);
696
+ const input = {
697
+ repo: t.repo.id,
698
+ rules: rules.rules,
699
+ orgPack: t.ctx.orgPack,
700
+ constitutionVersion: rules.constitutionVersion,
701
+ team: t.repo.owner
702
+ };
703
+ const path = ".specify/memory/constitution.md";
704
+ const plan = constitutionPlan(await readFile2(join3(dir, path), "utf8").catch(() => void 0));
705
+ if (plan.mode === "refuse") {
706
+ r.kept.push(path);
707
+ r.problems.push(plan.reason);
708
+ } else {
709
+ const c = await writeAssistantFiles(dir, [
710
+ {
711
+ path,
712
+ content: plan.mode === "file" ? renderConstitution(input) : renderConstitutionSection(input),
713
+ source: "org-pack checklists and the Atticus constitution",
714
+ mode: plan.mode,
715
+ style: "html"
716
+ }
717
+ ], { ...o.force ? { force: true } : {} });
718
+ for (const x of c.results)
719
+ (x.action === "created" || x.action === "updated" ? r.written : x.action === "unchanged" ? r.unchanged : r.kept).push(x.path);
720
+ }
721
+ }
722
+ const af = await generateAssistantFiles(root, {
723
+ repo: t.repo.id,
724
+ dir,
725
+ ...o.force ? { force: true } : {}
726
+ });
727
+ r.assistantFiles = { results: af.results, upToDate: af.upToDate };
728
+ r.problems.push(...af.problems);
729
+ if (o.commit !== false && (r.written.length || af.results.some((x) => x.written))) {
730
+ const git = o.git ?? createGit();
731
+ await git(["add", "-A", "--", "."], dir);
732
+ const staged = (await git(["diff", "--cached", "--name-only"], dir)).stdout.trim();
733
+ if (staged) {
734
+ await git([
735
+ "commit",
736
+ "-q",
737
+ "-m",
738
+ `Hand down task ${t.task.id} of ${t.change.id}: ${t.task.title}`,
739
+ "-m",
740
+ `Atticus-Change: ${t.change.id}
741
+ Atticus-Task: ${t.task.id}`
742
+ ], dir);
743
+ r.commit = (await git(["rev-parse", "HEAD"], dir)).stdout.trim();
744
+ }
745
+ }
746
+ return r;
747
+ }
748
+
749
+ // packages/speckit/dist/drift.js
750
+ import { existsSync as existsSync2 } from "node:fs";
751
+ import { readFile as readFile4 } from "node:fs/promises";
752
+ import { join as join6 } from "node:path";
753
+
754
+ // packages/speckit/dist/rounds.js
755
+ var import_yaml2 = __toESM(require_dist(), 1);
756
+ import { readFile as readFile3 } from "node:fs/promises";
757
+ import { fileURLToPath } from "node:url";
758
+ import { join as join4 } from "node:path";
759
+ var __atticus_import_meta_url = __atticusUrl("packages/speckit/dist/rounds.js");
760
+ var sig = (fs) => fs.map((f) => `${fingerprint(f)}|${f.state}|${f.severity}`).sort().join("\n");
761
+ async function writeFullRound(root, change, gate, fresh, o) {
762
+ const rounds = await roundsOf(root, change, gate);
763
+ const last = rounds.at(-1);
764
+ const prev = last ? (await readRound(root, change, gate, last)).findings.map((s) => s.finding) : [];
765
+ const carried = prev.filter((f) => o.carry?.(f));
766
+ const byPrint = new Map(prev.map((f) => [fingerprint(f), f]));
767
+ const taken = new Set(carried.map((f) => f.id));
768
+ const next = [...carried];
769
+ for (const f of fresh) {
770
+ const before = byPrint.get(fingerprint(f));
771
+ let id = f.id;
772
+ for (let i = 2; taken.has(id); i++)
773
+ id = `${f.id}-${i}`;
774
+ taken.add(id);
775
+ const merged = Finding.safeParse({
776
+ ...f,
777
+ id,
778
+ createdAt: before?.createdAt ?? f.createdAt,
779
+ ...before?.resolution ? { state: before.state, resolution: before.resolution } : {}
780
+ });
781
+ next.push(merged.success ? merged.data : { ...f, id });
782
+ }
783
+ if (sig(next) === sig(prev) && (last !== void 0 || next.length === 0)) {
784
+ const findings2 = last ? (await readRound(root, change, gate, last)).findings : [];
785
+ return { gate, ...last ? { round: last } : {}, unchanged: true, findings: findings2 };
786
+ }
787
+ const round = (last ?? 0) + 1;
788
+ const findings = [];
789
+ for (const f of next.sort((a, b) => a.id.localeCompare(b.id))) {
790
+ const g = Finding.parse({ ...f, round, gate, change });
791
+ await writeFinding(root, g);
792
+ findings.push({ finding: g, path: `review/${gate}/${round}/${g.id}.yaml` });
793
+ }
794
+ await writeFileAtomic(join4(changeFolder(root, change), "review", gate, String(round), "meta", "round.yaml"), toCanonicalYaml({
795
+ schemaVersion: 1,
796
+ change,
797
+ gate,
798
+ round,
799
+ createdAt: o.now,
800
+ findings: findings.map((f) => ({
801
+ id: f.finding.id,
802
+ rule: f.finding.rule,
803
+ severity: f.finding.severity
804
+ })),
805
+ ...o.meta
806
+ }, ["schemaVersion", "change", "gate", "round", "createdAt"]));
807
+ return { gate, round, unchanged: false, findings };
808
+ }
809
+ var DRIFT_RULES_FILE = fileURLToPath(new URL("../rules/drift.yaml", __atticus_import_meta_url));
810
+ async function loadDriftRules() {
811
+ const pack = ChecklistPack.parse((0, import_yaml2.parse)(await readFile3(DRIFT_RULES_FILE, "utf8")));
812
+ return new Map(pack.rules.map((r) => [r.id, r]));
813
+ }
814
+
815
+ // packages/speckit/dist/worktree.js
816
+ import { existsSync } from "node:fs";
817
+ import { mkdir as mkdir2 } from "node:fs/promises";
818
+ import { dirname as dirname2, join as join5 } from "node:path";
819
+ function taskWorktreePath(root, change, task, repo) {
820
+ for (const [what, v] of [
821
+ ["task id", task],
822
+ ["repo id", repo]
823
+ ])
824
+ if (!RecordId.safeParse(v).success)
825
+ throw new SpecKitError("invalid", `${what} "${v}" must be a lowercase slug`);
826
+ return join5(changeWorktreesDir(root, change), ".tasks", task, repo);
827
+ }
828
+ async function openTaskWorktree(root, change, task, repo, git = createGit()) {
829
+ const path = taskWorktreePath(root, change, task, repo);
830
+ const branch = taskBranch(change, task);
831
+ if (existsSync(path))
832
+ return { path, branch, created: false };
833
+ const base = baseClonePath(root, repo);
834
+ if (!existsSync(base))
835
+ throw new SpecKitError("refused", `There is no base clone of ${repo}; run atticus workspace sync first.`);
836
+ const ok = async (ref) => git(["rev-parse", "--verify", "--quiet", ref], base).then(() => true, () => false);
837
+ await mkdir2(dirname2(path), { recursive: true });
838
+ if (await ok(`refs/heads/${branch}`))
839
+ await git(["worktree", "add", "--", path, branch], base);
840
+ else {
841
+ const from = changeBranch(change);
842
+ if (!await ok(`refs/heads/${from}`))
843
+ throw new SpecKitError("refused", `${repo} has no ${from} branch yet. Open the change first: atticus workspace open ${change} --repos ${repo}`);
844
+ await git(["worktree", "add", "--no-track", "-b", branch, "--", path, from], base);
845
+ }
846
+ return { path, branch, created: true };
847
+ }
848
+
849
+ // packages/speckit/dist/drift.js
850
+ var DRIFT_GATE = "drift";
851
+ var PAST_BUILD = /* @__PURE__ */ new Set(["qa", "release-ready", "released", "post-release-review"]);
852
+ var isPlanOnly = (r) => r.evidence.length > 0 && r.evidence.every((e) => (e.extractor ?? "").startsWith("speckit@"));
853
+ var norm = (p) => normalizePath(p ?? "");
854
+ var typeKey = (t) => t.toLowerCase().replace(/\s+/g, "");
855
+ var topicOf2 = (op) => /^(publish|subscribe|send|receive)\s+(\S+)/i.exec(op.trim())?.[2];
856
+ var slug = (s) => s.toLowerCase().replace(/[^a-z0-9.]+/g, "-").replace(/^-+|-+$/g, "");
857
+ async function readChangePlans(root, changeId) {
858
+ const repos = (await readChangeRepos(root, changeId))?.repos ?? [];
859
+ if (repos.length === 0)
860
+ return [];
861
+ const manifest = new Map((await loadRepos(root)).map((r) => [r.id, r]));
862
+ const files = await readChangeFiles(changeFolder(root, changeId));
863
+ const tasks = readChangeTasks(files);
864
+ const out = /* @__PURE__ */ new Map();
865
+ for (const r of repos) {
866
+ const m = manifest.get(r.id);
867
+ const component = m?.component ?? r.id;
868
+ const mine = tasks.filter((t) => t.repo === r.id).map((t) => t.id);
869
+ const dirs = [
870
+ join6(root, r.worktree),
871
+ ...mine.map((t) => taskWorktreePath(root, changeId, t, r.id))
872
+ ].filter((d) => existsSync2(d));
873
+ for (const dir of dirs) {
874
+ const features = await readSpecKitFeatures(fsRepo(r.id, dir, "0".repeat(40)), {
875
+ component,
876
+ ...m?.system ? { system: m.system } : {},
877
+ dialects: [],
878
+ scannedAt: "1970-01-01T00:00:00Z",
879
+ warn: () => void 0
880
+ });
881
+ for (const f of features)
882
+ if (mine.includes(f.feature))
883
+ out.set(`${r.id}/${f.feature}`, {
884
+ repo: r.id,
885
+ component,
886
+ from: dir.slice(root.length + 1).split("\\").join("/"),
887
+ feature: f
888
+ });
889
+ }
890
+ }
891
+ return [...out.values()].sort((a, b) => `${a.repo}/${a.feature.feature}`.localeCompare(`${b.repo}/${b.feature.feature}`));
892
+ }
893
+ async function driftCheck(root, changeId, o = {}) {
894
+ const now = o.now ?? (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
895
+ const ctx = await loadChangeContext(root);
896
+ const change = await changeStore(root).read(changeId).catch((e) => {
897
+ throw new SpecKitError("not-found", e.message);
898
+ });
899
+ const rules = await loadDriftRules();
900
+ const files = await readChangeFiles(changeFolder(root, change.id));
901
+ const hops = readHops(files);
902
+ const data = readDataChanges(files);
903
+ const patterns = new Map(ctx.orgPack.patterns.map((p) => [p.code, p]));
904
+ const plans = await readChangePlans(root, change.id);
905
+ const code = ctx.records.filter((r) => !isPlanOnly(r) && !r.status.startsWith("planned-"));
906
+ const codeOf = (k) => code.filter((r) => r.kind === k);
907
+ const codeInterfaces = codeOf("Interface");
908
+ const codeEndpoints = codeOf("Endpoint");
909
+ const codeIds = new Set(code.map((r) => `${r.kind}/${r.id}`));
910
+ const systemOf = (id) => {
911
+ const c = code.find((r) => r.kind === "Component" && r.id === id);
912
+ return c?.system;
913
+ };
914
+ const designParties = /* @__PURE__ */ new Set([
915
+ ...change.systems,
916
+ ...hops.flatMap((h) => [h.caller, h.callee])
917
+ ]);
918
+ const knownToDesign = (id) => designParties.has(id) || systemOf(id) !== void 0 && designParties.has(systemOf(id));
919
+ const found = [];
920
+ const doc = (name) => docOf(files, name);
921
+ const quote = (lines, n) => {
922
+ const t = lines?.[n - 1]?.trim();
923
+ return t ? t.length <= 190 ? t : `${t.slice(0, 187)}...` : void 0;
924
+ };
925
+ const planText = /* @__PURE__ */ new Map();
926
+ for (const p of plans)
927
+ for (const f of p.feature.files)
928
+ planText.set(`${p.repo}/${f}`, (await readFile4(join6(root, p.from, f), "utf8")).replace(/\r\n/g, "\n").split("\n"));
929
+ const specEv = (p, r) => {
930
+ const e = r.evidence[0];
931
+ const q = e.path && e.line ? quote(planText.get(`${p.repo}/${e.path}`), e.line) : void 0;
932
+ return {
933
+ source: "spec",
934
+ repo: p.repo,
935
+ ...e.path ? { path: e.path } : {},
936
+ ...e.line ? { line: e.line } : {},
937
+ ...q ? { quote: q } : {}
938
+ };
939
+ };
940
+ const artEv = (name, line2) => {
941
+ const q = quote(doc(name)?.rawLines, line2);
942
+ return { source: "artifact", path: name, line: line2, ...q ? { quote: q } : {} };
943
+ };
944
+ const add = (ruleId, subject, f) => {
945
+ const rule = rules.get(ruleId);
946
+ found.push(Finding.parse({
947
+ schemaVersion: 1,
948
+ id: `${ruleId.replace(/^drift\./, "")}-${slug(subject)}`.slice(0, 120),
949
+ kind: "issue",
950
+ change: change.id,
951
+ gate: DRIFT_GATE,
952
+ round: 1,
953
+ rule: rule.id,
954
+ layer: rule.layer,
955
+ lens: rule.lens,
956
+ severity: rule.severity,
957
+ artifact: f.artifact,
958
+ locator: f.locator,
959
+ summary: f.summary,
960
+ why: rule.description ?? rule.title,
961
+ fix: {
962
+ text: rule.lens === "architecture" ? "Amend the change's design through its hand-off, or change the repo plan back to the design." : "Check which is right, the plan or the code, and fix the other."
963
+ },
964
+ evidence: f.evidence,
965
+ waivable: false,
966
+ state: "open",
967
+ createdAt: now
968
+ }));
969
+ };
970
+ const designHopFor = (from, to, method, path) => hops.find((h) => h.caller === from && h.callee === to && (method ? h.method === method && norm(h.path) === norm(path) : topicOf2(h.operation) === path));
971
+ const designColumn = (table, column) => {
972
+ for (const d of data)
973
+ if (d.tableId === table) {
974
+ for (const c of d.newColumns)
975
+ if (c.name.toLowerCase() === column.toLowerCase())
976
+ return {
977
+ d,
978
+ type: c.type.replace(/\s*\bnot null\b.*$/i, "").replace(/\s*\bdefault\b.*$/i, "").trim()
979
+ };
980
+ }
981
+ return void 0;
982
+ };
983
+ for (const p of plans) {
984
+ const where = (r) => `${r.evidence[0]?.path ?? p.feature.dir} in ${p.repo}`;
985
+ for (const raw of p.feature.records) {
986
+ const r = raw;
987
+ if (r.kind === "Interface") {
988
+ const i = r;
989
+ const pathOrTopic = i.normalizedPath;
990
+ const h = designHopFor(i.from, i.to, i.method, pathOrTopic);
991
+ const shown = i.method ? `${i.method} ${h?.path ?? pathOrTopic}` : `publish ${pathOrTopic}`;
992
+ if (h) {
993
+ const dv = viaOf(patterns, h.pattern);
994
+ const reasons = [];
995
+ if (dv && i.via !== "other" && i.via !== dv)
996
+ reasons.push(`${i.via}; the approved design says ${dv} (${h.pattern})`);
997
+ else if (i.pattern?.code && h.pattern && i.pattern.code !== h.pattern)
998
+ reasons.push(`with pattern ${i.pattern.code}; the approved design says ${h.pattern}`);
999
+ if (reasons.length)
1000
+ add("drift.plan-contradicts-design", i.id, {
1001
+ artifact: "interfaces.md",
1002
+ locator: { line: h.line },
1003
+ summary: `${where(i)} calls ${i.to} ${shown} ${reasons[0]}.`,
1004
+ evidence: [artEv("interfaces.md", h.line), specEv(p, i)]
1005
+ });
1006
+ } else {
1007
+ const c = codeInterfaces.find((x) => x.from === i.from && x.to === i.to && (x.method ?? "") === (i.method ?? "") && norm(x.normalizedPath) === norm(pathOrTopic));
1008
+ if (c) {
1009
+ if (i.via !== "other" && c.via !== i.via)
1010
+ add("drift.plan-vs-code", i.id, {
1011
+ artifact: "catalog",
1012
+ locator: { record: c.id },
1013
+ summary: `${where(i)} calls ${i.to} ${shown} ${i.via}; the code calls it ${c.via}.`,
1014
+ evidence: [specEv(p, i), { source: "catalog", record: c.id }]
1015
+ });
1016
+ } else if (!knownToDesign(i.to))
1017
+ add("drift.plan-adds-system", i.id, {
1018
+ artifact: "interfaces.md",
1019
+ locator: { record: i.id },
1020
+ summary: `${where(i)} calls ${i.to} (${shown}), a system the design doesn't include.`,
1021
+ evidence: [specEv(p, i)]
1022
+ });
1023
+ else
1024
+ add("drift.plan-adds-interface", i.id, {
1025
+ artifact: "interfaces.md",
1026
+ locator: { record: i.id },
1027
+ summary: `${where(i)} adds ${i.from} to ${i.to} (${shown}), which the design doesn't have.`,
1028
+ evidence: [specEv(p, i)]
1029
+ });
1030
+ }
1031
+ if (PAST_BUILD.has(change.state) && i.status === "planned-new" && !codeInterfaces.some((x) => x.id === i.id))
1032
+ add("drift.plan-not-built", i.id, {
1033
+ artifact: "catalog",
1034
+ locator: { record: i.id },
1035
+ summary: `${where(i)} plans ${i.from} to ${i.to} (${shown}); the code doesn't have it.`,
1036
+ evidence: [specEv(p, i)]
1037
+ });
1038
+ } else if (r.kind === "Endpoint") {
1039
+ const e = r;
1040
+ if (e.component !== p.component)
1041
+ continue;
1042
+ const rest = e.endpointType === "rest";
1043
+ const inDesign = rest ? hops.some((h) => h.callee === p.component && h.method === e.method && norm(h.path) === norm(e.pathOrTopic)) : hops.some((h) => h.caller === p.component && topicOf2(h.operation) === e.pathOrTopic);
1044
+ const inCode = codeEndpoints.some((x) => rest ? x.component === e.component && x.method === e.method && norm(x.normalizedPath ?? x.pathOrTopic) === norm(e.pathOrTopic) : x.endpointType === "event-topic" && x.pathOrTopic === e.pathOrTopic);
1045
+ const shown = rest ? `${e.method} ${e.pathOrTopic}` : `topic ${e.pathOrTopic}`;
1046
+ if (!inDesign && (!inCode || e.status !== "planned-new"))
1047
+ add("drift.plan-adds-interface", e.id, {
1048
+ artifact: "interfaces.md",
1049
+ locator: { record: e.id },
1050
+ summary: inCode ? `${where(e)} changes ${shown}, which the design doesn't plan to change.` : `${where(e)} serves ${shown}, which the design doesn't have.`,
1051
+ evidence: [specEv(p, e)]
1052
+ });
1053
+ if (PAST_BUILD.has(change.state) && e.status === "planned-new" && !inCode)
1054
+ add("drift.plan-not-built", e.id, {
1055
+ artifact: "catalog",
1056
+ locator: { record: e.id },
1057
+ summary: `${where(e)} plans ${shown}; the code doesn't have it.`,
1058
+ evidence: [specEv(p, e)]
1059
+ });
1060
+ } else if (r.kind === "Table") {
1061
+ const t = r;
1062
+ const inDesign = data.some((d) => d.tableId === t.id);
1063
+ const inCode = codeIds.has(`Table/${t.id}`);
1064
+ if (!inDesign && (!inCode || t.status !== "planned-new"))
1065
+ add("drift.plan-adds-data", t.id, {
1066
+ artifact: "data.md",
1067
+ locator: { record: t.id },
1068
+ summary: inCode ? `${where(t)} changes table ${t.id}, which data.md doesn't.` : `${where(t)} adds table ${t.id}, which data.md doesn't have.`,
1069
+ evidence: [specEv(p, t)]
1070
+ });
1071
+ } else if (r.kind === "Column") {
1072
+ const c = r;
1073
+ const tableName = c.table.split(".").slice(1).join(".");
1074
+ const dc = designColumn(c.table, c.name);
1075
+ const codeCol = code.find((x) => x.kind === "Column" && x.id === c.id);
1076
+ if (dc) {
1077
+ if (typeKey(dc.type) !== typeKey(c.type))
1078
+ add("drift.plan-contradicts-design", c.id, {
1079
+ artifact: "data.md",
1080
+ locator: { line: dc.d.line },
1081
+ summary: `${where(c)} makes ${dc.d.table || tableName}.${c.name} ${c.type}; data.md says ${dc.type}.`,
1082
+ evidence: [artEv("data.md", dc.d.line), specEv(p, c)]
1083
+ });
1084
+ } else if (codeCol) {
1085
+ if (typeKey(codeCol.type) !== typeKey(c.type))
1086
+ add("drift.plan-vs-code", c.id, {
1087
+ artifact: "catalog",
1088
+ locator: { record: codeCol.id },
1089
+ summary: `${where(c)} makes ${tableName}.${c.name} ${c.type}; the code has ${codeCol.type}.`,
1090
+ evidence: [specEv(p, c), { source: "catalog", record: codeCol.id }]
1091
+ });
1092
+ } else if (data.some((d) => d.tableId === c.table) || codeIds.has(`Table/${c.table}`)) {
1093
+ add("drift.plan-adds-data", c.id, {
1094
+ artifact: "data.md",
1095
+ locator: { record: c.id },
1096
+ summary: `${where(c)} adds column ${tableName}.${c.name}, which data.md doesn't have.`,
1097
+ evidence: [specEv(p, c)]
1098
+ });
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ const codeIfIds = new Set(codeInterfaces.map((i) => i.id));
1104
+ for (const h of hops) {
1105
+ const shown = `${h.caller} to ${h.callee} (${h.operation.trim()})`;
1106
+ if (/^if-/.test(h.id) && /planned-(modify|retire)/.test(h.status) && !codeIfIds.has(h.id))
1107
+ add("drift.design-vs-code", h.id, {
1108
+ artifact: "interfaces.md",
1109
+ locator: { line: h.line },
1110
+ summary: `interfaces.md changes ${h.id}, ${shown}, which the code doesn't have.`,
1111
+ evidence: [artEv("interfaces.md", h.line)]
1112
+ });
1113
+ if (!PAST_BUILD.has(change.state))
1114
+ continue;
1115
+ const there = codeInterfaces.some((x) => x.from === h.caller && x.to === h.callee && (h.method ? x.method === h.method && norm(x.normalizedPath) === norm(h.path) : x.normalizedPath === topicOf2(h.operation)));
1116
+ if (h.status === "planned-new" && !there)
1117
+ add("drift.design-vs-code", `${h.caller}-${h.callee}-${h.line}`, {
1118
+ artifact: "interfaces.md",
1119
+ locator: { line: h.line },
1120
+ summary: `The design plans ${shown}; the code doesn't have it.`,
1121
+ evidence: [artEv("interfaces.md", h.line)]
1122
+ });
1123
+ if (h.status === "planned-retire" && there)
1124
+ add("drift.design-vs-code", `${h.caller}-${h.callee}-${h.line}`, {
1125
+ artifact: "interfaces.md",
1126
+ locator: { line: h.line },
1127
+ summary: `The design retires ${shown}; the code still has it.`,
1128
+ evidence: [artEv("interfaces.md", h.line)]
1129
+ });
1130
+ }
1131
+ const unique = [...new Map(found.map((f) => [f.id, f])).values()].sort((a, b) => a.id.localeCompare(b.id));
1132
+ if (o.write === false)
1133
+ return {
1134
+ change: change.id,
1135
+ gate: DRIFT_GATE,
1136
+ unchanged: true,
1137
+ findings: [],
1138
+ plans,
1139
+ found: unique
1140
+ };
1141
+ const w = await writeFullRound(root, change.id, DRIFT_GATE, unique, {
1142
+ now,
1143
+ meta: { plans: plans.map((p) => ({ repo: p.repo, feature: p.feature.feature, from: p.from })) }
1144
+ });
1145
+ return { ...w, change: change.id, plans, found: unique };
1146
+ }
1147
+
1148
+ // packages/speckit/dist/handup.js
1149
+ var import_yaml3 = __toESM(require_dist(), 1);
1150
+ import { existsSync as existsSync3 } from "node:fs";
1151
+ import { readdir, readFile as readFile5 } from "node:fs/promises";
1152
+ import { join as join7 } from "node:path";
1153
+ var HAND_UP_GATE = "hand-up";
1154
+ var HAND_UP_LENS = "complete";
1155
+ var INSTRUCTIONS = (change, task, repo) => `# Critic: the complete lens (Is it complete?), at hand-up
1156
+
1157
+ You are the complete lens of the Atticus critic, in a fresh session. Task ${task} of change ${change} was carried out in ${repo}, and its pull request is raised. Review the repo's Spec Kit plan.md, tasks.md and spec.md, and the tests the task added or changed, given below as data, for what they leave out: a scenario no test covers, a behaviour change with no test before it, an error path nobody wrote. Apply only the rules listed and report nothing else.
1158
+
1159
+ Lines are numbered as \`<n>: <text>\`; cite those numbers.
1160
+
1161
+ Report each problem as a finding, in YAML, and write nothing else:
1162
+
1163
+ findings:
1164
+ - rule: <a rule id from the list>
1165
+ kind: issue # or knowledge-gap, when only a person can answer
1166
+ artifact: <the data block's id>
1167
+ locator: { line: <line number>, heading: <section heading> }
1168
+ summary: <one sentence>
1169
+ why: <why it matters>
1170
+ fix: <what to change>
1171
+ evidence:
1172
+ - { path: <the data block's id>, line: <line number>, quote: <the exact words on that line> }
1173
+ question: { text: <the question>, owner: <who should answer> } # knowledge gaps only
1174
+
1175
+ Every finding quotes the exact text it relies on; one whose quote isn't on that line is discarded. Severity comes from the rule. Write "findings: []" when nothing is wrong.`;
1176
+ var numbered = (d) => d.rawLines.map((l, i) => `${i + 1}: ${l}`).join("\n");
1177
+ async function findPr(o, repo, heads) {
1178
+ if (o.pr)
1179
+ return { ...o.pr, state: "open", head: heads[0] };
1180
+ if (!o.gitHost)
1181
+ return void 0;
1182
+ for (const head of heads) {
1183
+ const prs = await o.gitHost.listPullRequests(repo, { head });
1184
+ const pr = prs.find((p) => p.state !== "closed");
1185
+ if (pr)
1186
+ return { number: pr.number, url: pr.url, state: pr.state, head };
1187
+ }
1188
+ return void 0;
1189
+ }
1190
+ var handUpLensOutput = (task) => `tasks/${task}/review-${HAND_UP_LENS}.yaml`;
1191
+ var TEST_FILE = /(^|\/)(src\/test|tests?|__tests__|spec)\/|(\.|_)(test|spec)\.[cm]?[jt]sx?$|(Test|Tests|IT)\.(java|kt|cs)$|_test\.(go|py|rb)$|^test_.*\.py$|\/test_[^/]*\.py$|\.feature$/;
1192
+ function handUpLensTask(outputPath) {
1193
+ return /^tasks\/([^/]+)\/review-(?:complete|code-and-test)\.yaml$/.exec(outputPath)?.[1];
1194
+ }
1195
+ function reviewsTask(r) {
1196
+ if (r.check.type !== "ai" || questionOfRule(r) !== HAND_UP_LENS)
1197
+ return false;
1198
+ return r.lens === "code-and-test" || r.appliesTo.artifacts.some((a) => a === "code" || a === "tests");
1199
+ }
1200
+ async function lensSetup(root, t, now) {
1201
+ const gate = {
1202
+ id: HAND_UP_GATE,
1203
+ phase: "build",
1204
+ approvers: ["developer"],
1205
+ minApprovals: 1,
1206
+ checklists: [],
1207
+ checks: [],
1208
+ maxAuthorRounds: 2
1209
+ };
1210
+ const criticCtx = buildContext({
1211
+ root,
1212
+ change: t.change,
1213
+ type: changeTypeOf(t.ctx, t.change.type),
1214
+ lifecycle: t.ctx,
1215
+ footprint: await changeFootprint(t.ctx, t.change),
1216
+ files: t.files,
1217
+ gate,
1218
+ now
1219
+ });
1220
+ const { rules: all } = await rulesForRepo(root, t.repo.owner);
1221
+ const rules = all.filter(reviewsTask);
1222
+ return { criticCtx, rules };
1223
+ }
1224
+ async function applyLensRun(root, a) {
1225
+ const { t, now } = a;
1226
+ const left = new Set(a.run.request.pack.manifest.dropped.map((d) => d.id));
1227
+ const parsed = parseLensOutput(a.criticCtx, HAND_UP_LENS, a.run.output ?? "", a.rules, a.docs.filter((d) => !left.has(d.path)));
1228
+ const taken = /* @__PURE__ */ new Set();
1229
+ const prefix = `t${t.task.id.replace(/[^a-z0-9]+/g, "-")}-`;
1230
+ const fresh = parsed.hits.map((h) => toFinding(h, {
1231
+ id: `${prefix}${findingIdFor(h, taken)}`.slice(0, 120),
1232
+ change: t.change.id,
1233
+ gate: HAND_UP_GATE,
1234
+ round: 1,
1235
+ now
1236
+ }));
1237
+ const review = await writeFullRound(root, t.change.id, HAND_UP_GATE, fresh, {
1238
+ now,
1239
+ carry: (f) => !f.artifact.startsWith(`tasks/${t.task.id}/`),
1240
+ meta: {
1241
+ task: t.task.id,
1242
+ repo: t.repo.id,
1243
+ ...a.pr !== void 0 ? { pr: a.pr } : {},
1244
+ runId: a.run.runId
1245
+ }
1246
+ });
1247
+ return { discarded: parsed.discarded, review };
1248
+ }
1249
+ async function completeHandUpLens(root, run, o = {}) {
1250
+ const task = handUpLensTask(run.request.outputPath);
1251
+ if (!task)
1252
+ throw new SpecKitError("refused", `Run ${run.runId} isn't a hand-up review of a task.`);
1253
+ const now = o.now ?? (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
1254
+ const t = await loadTask(root, run.request.change, task);
1255
+ const folder = changeFolder(root, t.change.id);
1256
+ const evidenceDir = join7(folder, `tasks/${task}/evidence`);
1257
+ const docs = [];
1258
+ for (const name of (await readdir(evidenceDir).catch(() => [])).sort())
1259
+ if (name.endsWith(".md"))
1260
+ docs.push(parseDoc(`tasks/${task}/evidence/${name}`, await readFile5(join7(evidenceDir, name), "utf8")));
1261
+ const source = (0, import_yaml3.parse)(await readFile5(join7(evidenceDir, "source.yaml"), "utf8").catch(() => "{}"));
1262
+ const { criticCtx, rules } = await lensSetup(root, t, now);
1263
+ const summary = toRunSummary(run);
1264
+ await writeFileAtomic(join7(folder, `tasks/${task}/runs/${run.runId}.yaml`), toCanonicalYaml(summary, ["schemaVersion", "runId"]));
1265
+ const lens = {
1266
+ state: "ran",
1267
+ runId: run.runId,
1268
+ agent: run.request.agent.id,
1269
+ rules: rules.map((r) => r.id),
1270
+ discarded: [],
1271
+ summary
1272
+ };
1273
+ if (run.state !== "validated" || run.output === void 0) {
1274
+ lens.state = "failed";
1275
+ lens.reason = run.errors.join("; ") || `the run ended ${run.state}`;
1276
+ return { change: t.change.id, task, lens };
1277
+ }
1278
+ const applied = await applyLensRun(root, {
1279
+ t,
1280
+ criticCtx,
1281
+ rules,
1282
+ docs,
1283
+ run,
1284
+ ...source?.pr?.number !== void 0 ? { pr: source.pr.number } : {},
1285
+ now
1286
+ });
1287
+ lens.discarded = applied.discarded;
1288
+ return { change: t.change.id, task, lens, review: applied.review };
1289
+ }
1290
+ async function handUp(root, changeId, taskId, o) {
1291
+ const now = o.now ?? (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
1292
+ const t = await loadTask(root, changeId, taskId);
1293
+ const heads = [taskBranch(t.change.id, t.task.id), changeBranch(t.change.id)];
1294
+ const pr = await findPr(o, t.repo.id, heads);
1295
+ if (!pr)
1296
+ throw new SpecKitError("refused", `The pull request for task ${t.task.id} isn't raised yet (looked for ${heads.join(" and ")} in ${t.repo.id}). Hand-up runs once it is; with no Git host connected, record it with --pr <number> --pr-url <url>.`);
1297
+ const taskDir = taskWorktreePath(root, t.change.id, t.task.id, t.repo.id);
1298
+ const dir = existsSync3(taskDir) ? taskDir : await changeWorktree(root, t.change.id, t.repo.id);
1299
+ const engine = buildEngineFor(t.ctx.orgPack, t.repo.id);
1300
+ const wanted = engine === "speckit" ? ["plan.md", "tasks.md", "spec.md"].map((f) => `specs/${t.task.id}/${f}`) : [`.atticus/tasks/${t.task.id}.md`];
1301
+ const git = o.git ?? createGit();
1302
+ const sha = await git(["rev-parse", "HEAD"], dir).then((r) => r.stdout.trim(), () => void 0);
1303
+ const folder = changeFolder(root, t.change.id);
1304
+ const evidence = [];
1305
+ const docs = [];
1306
+ for (const rel of wanted) {
1307
+ const text = await readFile5(join7(dir, rel), "utf8").catch(() => void 0);
1308
+ if (text === void 0)
1309
+ continue;
1310
+ const to = `tasks/${t.task.id}/evidence/${rel.split("/").pop()}`;
1311
+ await writeFileAtomic(join7(folder, to), text);
1312
+ evidence.push(to);
1313
+ docs.push(parseDoc(to, text));
1314
+ }
1315
+ const base = await git(["merge-base", "HEAD", t.repo.defaultBranch ?? "main"], dir).then((r) => r.stdout.trim(), () => void 0);
1316
+ if (base) {
1317
+ const changed = await git(["diff", "--name-only", `${base}..HEAD`], dir).then((r) => r.stdout.split("\n").map((l) => l.trim()).filter(Boolean), () => []);
1318
+ const tests = changed.filter((f) => TEST_FILE.test(f)).sort().slice(0, 8);
1319
+ for (const rel of tests) {
1320
+ const text = await readFile5(join7(dir, rel), "utf8").catch(() => void 0);
1321
+ if (text === void 0 || text.length > 6e4)
1322
+ continue;
1323
+ const to = `tasks/${t.task.id}/evidence/tests/${rel.replace(/[\\/]/g, "__")}`;
1324
+ await writeFileAtomic(join7(folder, to), text);
1325
+ evidence.push(to);
1326
+ docs.push(parseDoc(to, text));
1327
+ }
1328
+ }
1329
+ if (engine === "speckit" && !evidence.some((e) => e.endsWith("/plan.md")))
1330
+ throw new SpecKitError("refused", `specs/${t.task.id}/plan.md isn't in ${t.repo.id}; hand the task down first: atticus spec hand-down ${t.change.id} ${t.task.id}`);
1331
+ await writeFileAtomic(join7(folder, `tasks/${t.task.id}/evidence/source.yaml`), toCanonicalYaml({
1332
+ schemaVersion: 1,
1333
+ change: t.change.id,
1334
+ task: t.task.id,
1335
+ repo: t.repo.id,
1336
+ branch: pr.head,
1337
+ ...sha ? { commit: sha } : {},
1338
+ pr: { number: pr.number, url: pr.url },
1339
+ files: wanted.filter((w) => evidence.some((e) => e.endsWith(`/${w.split("/").pop()}`))),
1340
+ ...evidence.some((e) => e.includes("/evidence/tests/")) ? { tests: evidence.filter((e) => e.includes("/evidence/tests/")) } : {},
1341
+ by: o.by,
1342
+ at: now
1343
+ }, ["schemaVersion", "change", "task", "repo", "branch", "commit"]));
1344
+ const markedDone = await markTaskDone(root, t.change.id, t.task);
1345
+ const lens = { state: "skipped", rules: [], discarded: [] };
1346
+ let review;
1347
+ const { criticCtx, rules } = await lensSetup(root, t, now);
1348
+ lens.rules = rules.map((r) => r.id);
1349
+ const agent = lensAgent(criticCtx, HAND_UP_LENS);
1350
+ lens.agent = agent.id;
1351
+ if (rules.length === 0)
1352
+ lens.reason = "no AI rule of the complete lens reviews a task's code or tests";
1353
+ else if (!o.runtime)
1354
+ lens.reason = "no AI runtime was given; review plan.md and tasks.md by hand";
1355
+ else if (!agent.inputs.some((i) => i === "tasks" || i === "repo-code"))
1356
+ lens.reason = `agent ${agent.id} reads neither tasks nor repo code`;
1357
+ else {
1358
+ const levels = criticCtx.classifications;
1359
+ const sources = [
1360
+ {
1361
+ id: "0-instructions",
1362
+ kind: "instruction",
1363
+ origin: "atticus/speckit",
1364
+ text: INSTRUCTIONS(t.change.id, t.task.id, t.repo.id),
1365
+ classification: "public",
1366
+ trusted: true
1367
+ },
1368
+ {
1369
+ id: "1-rules",
1370
+ kind: "checklist",
1371
+ origin: "checklists",
1372
+ text: `## Rules
1373
+
1374
+ ${toCanonicalYaml(rules.map((r) => ({
1375
+ id: r.id,
1376
+ title: r.title,
1377
+ severity: r.severity,
1378
+ look_for: r.check.type === "ai" ? r.check.prompt : ""
1379
+ })), ["id", "title", "severity", "look_for"]).trimEnd()}`,
1380
+ classification: "public",
1381
+ trusted: true
1382
+ },
1383
+ ...docs.map((d) => ({
1384
+ id: d.path,
1385
+ kind: "artifact",
1386
+ origin: `${t.repo.id}:${wanted.find((w) => w.endsWith(`/${d.path.split("/").pop()}`)) ?? d.path}`,
1387
+ text: numbered(d),
1388
+ classification: "internal",
1389
+ priority: 10
1390
+ }))
1391
+ ];
1392
+ try {
1393
+ const pack = buildContextPack({
1394
+ change: t.change.id,
1395
+ agent: agent.id,
1396
+ budget: t.ctx.orgPack.runtime.budgets.perAgent[agent.id] ?? agent.tokenBudget,
1397
+ sources,
1398
+ levels,
1399
+ maxClassification: o.maxClassification ?? (levels.includes("confidential") ? "confidential" : levels.at(-1)),
1400
+ now: new Date(now)
1401
+ });
1402
+ const prepared = await o.runtime.prepare({
1403
+ change: t.change.id,
1404
+ agent,
1405
+ pack,
1406
+ outputPath: handUpLensOutput(t.task.id),
1407
+ stepClass: "critic"
1408
+ });
1409
+ const run = await o.runtime.run(prepared.runId);
1410
+ lens.runId = run.runId;
1411
+ lens.summary = toRunSummary(run);
1412
+ await writeFileAtomic(join7(folder, `tasks/${t.task.id}/runs/${run.runId}.yaml`), toCanonicalYaml(lens.summary, ["schemaVersion", "runId"]));
1413
+ if (run.state === "waiting") {
1414
+ lens.state = "waiting";
1415
+ lens.reason = `handed to your assistant; finish with atticus handoff done ${run.runId}`;
1416
+ } else if (run.state !== "validated" || run.output === void 0) {
1417
+ lens.state = "failed";
1418
+ lens.reason = run.errors.join("; ") || `the run ended ${run.state}`;
1419
+ } else {
1420
+ const applied = await applyLensRun(root, {
1421
+ t,
1422
+ criticCtx,
1423
+ rules,
1424
+ docs,
1425
+ run,
1426
+ pr: pr.number,
1427
+ now
1428
+ });
1429
+ lens.state = "ran";
1430
+ lens.discarded = applied.discarded;
1431
+ review = applied.review;
1432
+ }
1433
+ } catch (e) {
1434
+ lens.state = "failed";
1435
+ lens.reason = e.message;
1436
+ }
1437
+ }
1438
+ const drift = await driftCheck(root, t.change.id, { now });
1439
+ return {
1440
+ change: t.change.id,
1441
+ task: t.task.id,
1442
+ repo: t.repo.id,
1443
+ pr,
1444
+ markedDone,
1445
+ evidence,
1446
+ lens,
1447
+ ...review ? { review } : {},
1448
+ drift
1449
+ };
1450
+ }
1451
+
1452
+ // packages/speckit/dist/after-scan.js
1453
+ var DRIFT_AFTER_SCAN_STATES = ["build", "qa"];
1454
+ async function driftAfterScan(root, o = {}) {
1455
+ const states = new Set(DRIFT_AFTER_SCAN_STATES);
1456
+ const open = (await changeStore(root).list()).filter((c) => states.has(c.state));
1457
+ const out = [];
1458
+ for (const c of open) {
1459
+ try {
1460
+ const r = await driftCheck(root, c.id, o.now ? { now: o.now } : {});
1461
+ const live = r.findings.filter((f) => f.finding.state === "open");
1462
+ out.push({
1463
+ change: c.id,
1464
+ state: c.state,
1465
+ ...r.round !== void 0 ? { round: r.round } : {},
1466
+ unchanged: r.unchanged,
1467
+ open: live.length,
1468
+ blocking: live.filter((f) => f.finding.severity === "blocking").length
1469
+ });
1470
+ } catch (e) {
1471
+ out.push({
1472
+ change: c.id,
1473
+ state: c.state,
1474
+ unchanged: true,
1475
+ open: 0,
1476
+ blocking: 0,
1477
+ error: e.message
1478
+ });
1479
+ }
1480
+ }
1481
+ return out;
1482
+ }
1483
+
1484
+ export {
1485
+ readChangeTasks,
1486
+ readStory,
1487
+ loadTask,
1488
+ changeWorktree,
1489
+ handDown,
1490
+ openTaskWorktree,
1491
+ driftCheck,
1492
+ handUpLensTask,
1493
+ completeHandUpLens,
1494
+ handUp,
1495
+ driftAfterScan
1496
+ };