@matteeh/osq 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (619) hide show
  1. package/README.md +415 -72
  2. package/dist/cli/approve.d.ts +13 -3
  3. package/dist/cli/approve.d.ts.map +1 -1
  4. package/dist/cli/approve.js +59 -4
  5. package/dist/cli/approve.js.map +1 -1
  6. package/dist/cli/bin.js +0 -0
  7. package/dist/cli/confirm.d.ts +21 -0
  8. package/dist/cli/confirm.d.ts.map +1 -0
  9. package/dist/cli/confirm.js +28 -0
  10. package/dist/cli/confirm.js.map +1 -0
  11. package/dist/cli/doctor.d.ts +13 -0
  12. package/dist/cli/doctor.d.ts.map +1 -0
  13. package/dist/cli/doctor.js +23 -0
  14. package/dist/cli/doctor.js.map +1 -0
  15. package/dist/cli/done.d.ts +12 -0
  16. package/dist/cli/done.d.ts.map +1 -0
  17. package/dist/cli/done.js +26 -0
  18. package/dist/cli/done.js.map +1 -0
  19. package/dist/cli/inbox.d.ts +22 -0
  20. package/dist/cli/inbox.d.ts.map +1 -0
  21. package/dist/cli/inbox.js +41 -0
  22. package/dist/cli/inbox.js.map +1 -0
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +127 -8
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/init.d.ts +1 -0
  27. package/dist/cli/init.d.ts.map +1 -1
  28. package/dist/cli/init.js +9 -3
  29. package/dist/cli/init.js.map +1 -1
  30. package/dist/cli/lint.d.ts +26 -0
  31. package/dist/cli/lint.d.ts.map +1 -0
  32. package/dist/cli/lint.js +60 -0
  33. package/dist/cli/lint.js.map +1 -0
  34. package/dist/cli/migrate.d.ts +18 -0
  35. package/dist/cli/migrate.d.ts.map +1 -0
  36. package/dist/cli/migrate.js +42 -0
  37. package/dist/cli/migrate.js.map +1 -0
  38. package/dist/cli/new.js +1 -1
  39. package/dist/cli/new.js.map +1 -1
  40. package/dist/cli/plan-queue.d.ts +50 -0
  41. package/dist/cli/plan-queue.d.ts.map +1 -0
  42. package/dist/cli/plan-queue.js +167 -0
  43. package/dist/cli/plan-queue.js.map +1 -0
  44. package/dist/cli/plan.d.ts +35 -0
  45. package/dist/cli/plan.d.ts.map +1 -0
  46. package/dist/cli/plan.js +153 -0
  47. package/dist/cli/plan.js.map +1 -0
  48. package/dist/cli/queue.d.ts +9 -0
  49. package/dist/cli/queue.d.ts.map +1 -0
  50. package/dist/cli/queue.js +24 -0
  51. package/dist/cli/queue.js.map +1 -0
  52. package/dist/cli/reject.d.ts +8 -0
  53. package/dist/cli/reject.d.ts.map +1 -0
  54. package/dist/cli/reject.js +18 -0
  55. package/dist/cli/reject.js.map +1 -0
  56. package/dist/cli/report.d.ts +1 -1
  57. package/dist/cli/report.d.ts.map +1 -1
  58. package/dist/cli/report.js +141 -14
  59. package/dist/cli/report.js.map +1 -1
  60. package/dist/cli/retry.d.ts +7 -0
  61. package/dist/cli/retry.d.ts.map +1 -0
  62. package/dist/cli/retry.js +29 -0
  63. package/dist/cli/retry.js.map +1 -0
  64. package/dist/cli/serve.d.ts +30 -0
  65. package/dist/cli/serve.d.ts.map +1 -0
  66. package/dist/cli/serve.js +136 -0
  67. package/dist/cli/serve.js.map +1 -0
  68. package/dist/cli/setup.d.ts.map +1 -1
  69. package/dist/cli/setup.js +7 -1
  70. package/dist/cli/setup.js.map +1 -1
  71. package/dist/cli/show.d.ts +2 -1
  72. package/dist/cli/show.d.ts.map +1 -1
  73. package/dist/cli/show.js +20 -3
  74. package/dist/cli/show.js.map +1 -1
  75. package/dist/cli/status.d.ts +1 -1
  76. package/dist/cli/status.d.ts.map +1 -1
  77. package/dist/cli/status.js +2 -2
  78. package/dist/cli/status.js.map +1 -1
  79. package/dist/cli/watch.d.ts +9 -6
  80. package/dist/cli/watch.d.ts.map +1 -1
  81. package/dist/cli/watch.js +15 -2
  82. package/dist/cli/watch.js.map +1 -1
  83. package/dist/core/foundation/config-codex.d.ts +22 -0
  84. package/dist/core/foundation/config-codex.d.ts.map +1 -0
  85. package/dist/core/foundation/config-codex.js +58 -0
  86. package/dist/core/foundation/config-codex.js.map +1 -0
  87. package/dist/core/foundation/config-doctor.d.ts +14 -0
  88. package/dist/core/foundation/config-doctor.d.ts.map +1 -0
  89. package/dist/core/foundation/config-doctor.js +29 -0
  90. package/dist/core/foundation/config-doctor.js.map +1 -0
  91. package/dist/core/foundation/config-env.d.ts +3 -0
  92. package/dist/core/foundation/config-env.d.ts.map +1 -0
  93. package/dist/core/foundation/config-env.js +22 -0
  94. package/dist/core/foundation/config-env.js.map +1 -0
  95. package/dist/core/foundation/config-gates.d.ts +22 -0
  96. package/dist/core/foundation/config-gates.d.ts.map +1 -0
  97. package/dist/core/foundation/config-gates.js +43 -0
  98. package/dist/core/foundation/config-gates.js.map +1 -0
  99. package/dist/core/foundation/config-pi.d.ts +64 -0
  100. package/dist/core/foundation/config-pi.d.ts.map +1 -0
  101. package/dist/core/foundation/config-pi.js +131 -0
  102. package/dist/core/foundation/config-pi.js.map +1 -0
  103. package/dist/core/foundation/config-planning.d.ts +24 -0
  104. package/dist/core/foundation/config-planning.d.ts.map +1 -0
  105. package/dist/core/foundation/config-planning.js +60 -0
  106. package/dist/core/foundation/config-planning.js.map +1 -0
  107. package/dist/core/foundation/config-queue.d.ts +15 -0
  108. package/dist/core/foundation/config-queue.d.ts.map +1 -0
  109. package/dist/core/foundation/config-queue.js +24 -0
  110. package/dist/core/foundation/config-queue.js.map +1 -0
  111. package/dist/core/foundation/config-serve.d.ts +20 -0
  112. package/dist/core/foundation/config-serve.d.ts.map +1 -0
  113. package/dist/core/foundation/config-serve.js +35 -0
  114. package/dist/core/foundation/config-serve.js.map +1 -0
  115. package/dist/core/{config.d.ts → foundation/config.d.ts} +34 -3
  116. package/dist/core/foundation/config.d.ts.map +1 -0
  117. package/dist/core/{config.js → foundation/config.js} +35 -18
  118. package/dist/core/foundation/config.js.map +1 -0
  119. package/dist/core/foundation/doctor-managed.d.ts +13 -0
  120. package/dist/core/foundation/doctor-managed.d.ts.map +1 -0
  121. package/dist/core/foundation/doctor-managed.js +77 -0
  122. package/dist/core/foundation/doctor-managed.js.map +1 -0
  123. package/dist/core/foundation/doctor.d.ts +19 -0
  124. package/dist/core/foundation/doctor.d.ts.map +1 -0
  125. package/dist/core/foundation/doctor.js +187 -0
  126. package/dist/core/foundation/doctor.js.map +1 -0
  127. package/dist/core/foundation/harness-catalog.d.ts +96 -0
  128. package/dist/core/foundation/harness-catalog.d.ts.map +1 -0
  129. package/dist/core/foundation/harness-catalog.js +182 -0
  130. package/dist/core/foundation/harness-catalog.js.map +1 -0
  131. package/dist/core/foundation/init-blocks.d.ts +33 -0
  132. package/dist/core/foundation/init-blocks.d.ts.map +1 -0
  133. package/dist/core/foundation/init-blocks.js +165 -0
  134. package/dist/core/foundation/init-blocks.js.map +1 -0
  135. package/dist/core/foundation/init-managed.d.ts +10 -0
  136. package/dist/core/foundation/init-managed.d.ts.map +1 -0
  137. package/dist/core/foundation/init-managed.js +115 -0
  138. package/dist/core/foundation/init-managed.js.map +1 -0
  139. package/dist/core/foundation/init.d.ts +18 -0
  140. package/dist/core/foundation/init.d.ts.map +1 -0
  141. package/dist/core/foundation/init.js +113 -0
  142. package/dist/core/foundation/init.js.map +1 -0
  143. package/dist/core/{logger.d.ts → foundation/logger.d.ts} +1 -0
  144. package/dist/core/foundation/logger.d.ts.map +1 -0
  145. package/dist/core/{logger.js → foundation/logger.js} +18 -2
  146. package/dist/core/foundation/logger.js.map +1 -0
  147. package/dist/core/{new.d.ts → foundation/new.d.ts} +3 -0
  148. package/dist/core/foundation/new.d.ts.map +1 -0
  149. package/dist/core/foundation/new.js +206 -0
  150. package/dist/core/foundation/new.js.map +1 -0
  151. package/dist/core/foundation/package-root.d.ts +9 -0
  152. package/dist/core/foundation/package-root.d.ts.map +1 -0
  153. package/dist/core/foundation/package-root.js +11 -0
  154. package/dist/core/foundation/package-root.js.map +1 -0
  155. package/dist/core/lifecycle/done.d.ts +17 -0
  156. package/dist/core/lifecycle/done.d.ts.map +1 -0
  157. package/dist/core/lifecycle/done.js +97 -0
  158. package/dist/core/lifecycle/done.js.map +1 -0
  159. package/dist/core/lifecycle/reject.d.ts +26 -0
  160. package/dist/core/lifecycle/reject.d.ts.map +1 -0
  161. package/dist/core/lifecycle/reject.js +83 -0
  162. package/dist/core/lifecycle/reject.js.map +1 -0
  163. package/dist/core/lifecycle/retry-transition.d.ts +23 -0
  164. package/dist/core/lifecycle/retry-transition.d.ts.map +1 -0
  165. package/dist/core/lifecycle/retry-transition.js +41 -0
  166. package/dist/core/lifecycle/retry-transition.js.map +1 -0
  167. package/dist/core/lifecycle/retry.d.ts +18 -0
  168. package/dist/core/lifecycle/retry.d.ts.map +1 -0
  169. package/dist/core/lifecycle/retry.js +179 -0
  170. package/dist/core/lifecycle/retry.js.map +1 -0
  171. package/dist/core/report/approval-flags.d.ts +29 -0
  172. package/dist/core/report/approval-flags.d.ts.map +1 -0
  173. package/dist/core/report/approval-flags.js +138 -0
  174. package/dist/core/report/approval-flags.js.map +1 -0
  175. package/dist/core/report/planning-economics.d.ts +39 -0
  176. package/dist/core/report/planning-economics.d.ts.map +1 -0
  177. package/dist/core/report/planning-economics.js +168 -0
  178. package/dist/core/report/planning-economics.js.map +1 -0
  179. package/dist/core/report/planning-observed.d.ts +59 -0
  180. package/dist/core/report/planning-observed.d.ts.map +1 -0
  181. package/dist/core/report/planning-observed.js +135 -0
  182. package/dist/core/report/planning-observed.js.map +1 -0
  183. package/dist/core/report/planning-records.d.ts +69 -0
  184. package/dist/core/report/planning-records.d.ts.map +1 -0
  185. package/dist/core/report/planning-records.js +149 -0
  186. package/dist/core/report/planning-records.js.map +1 -0
  187. package/dist/core/report/planning-slice-lookup.d.ts +13 -0
  188. package/dist/core/report/planning-slice-lookup.d.ts.map +1 -0
  189. package/dist/core/report/planning-slice-lookup.js +146 -0
  190. package/dist/core/report/planning-slice-lookup.js.map +1 -0
  191. package/dist/core/report/planning-slice-measures.d.ts +17 -0
  192. package/dist/core/report/planning-slice-measures.d.ts.map +1 -0
  193. package/dist/core/report/planning-slice-measures.js +85 -0
  194. package/dist/core/report/planning-slice-measures.js.map +1 -0
  195. package/dist/core/report/planning-slice-record.d.ts +4 -0
  196. package/dist/core/report/planning-slice-record.d.ts.map +1 -0
  197. package/dist/core/report/planning-slice-record.js +66 -0
  198. package/dist/core/report/planning-slice-record.js.map +1 -0
  199. package/dist/core/report/planning-slice-turns.d.ts +13 -0
  200. package/dist/core/report/planning-slice-turns.d.ts.map +1 -0
  201. package/dist/core/report/planning-slice-turns.js +41 -0
  202. package/dist/core/report/planning-slice-turns.js.map +1 -0
  203. package/dist/core/report/planning-slice.d.ts +70 -0
  204. package/dist/core/report/planning-slice.d.ts.map +1 -0
  205. package/dist/core/report/planning-slice.js +147 -0
  206. package/dist/core/report/planning-slice.js.map +1 -0
  207. package/dist/core/report/planning.d.ts +31 -0
  208. package/dist/core/report/planning.d.ts.map +1 -0
  209. package/dist/core/report/planning.js +75 -0
  210. package/dist/core/report/planning.js.map +1 -0
  211. package/dist/core/report/report-events.d.ts +52 -0
  212. package/dist/core/report/report-events.d.ts.map +1 -0
  213. package/dist/core/report/report-events.js +198 -0
  214. package/dist/core/report/report-events.js.map +1 -0
  215. package/dist/core/report/report-pre-spawn.d.ts +25 -0
  216. package/dist/core/report/report-pre-spawn.d.ts.map +1 -0
  217. package/dist/core/report/report-pre-spawn.js +40 -0
  218. package/dist/core/report/report-pre-spawn.js.map +1 -0
  219. package/dist/core/report/report-retries.d.ts +30 -0
  220. package/dist/core/report/report-retries.d.ts.map +1 -0
  221. package/dist/core/report/report-retries.js +100 -0
  222. package/dist/core/report/report-retries.js.map +1 -0
  223. package/dist/core/report/report.d.ts +276 -0
  224. package/dist/core/report/report.d.ts.map +1 -0
  225. package/dist/core/report/report.js +1421 -0
  226. package/dist/core/report/report.js.map +1 -0
  227. package/dist/core/{lock.d.ts → run/lock.d.ts} +6 -0
  228. package/dist/core/run/lock.d.ts.map +1 -0
  229. package/dist/core/{lock.js → run/lock.js} +7 -15
  230. package/dist/core/run/lock.js.map +1 -0
  231. package/dist/core/run/manifest.d.ts +39 -0
  232. package/dist/core/run/manifest.d.ts.map +1 -0
  233. package/dist/core/run/manifest.js +137 -0
  234. package/dist/core/run/manifest.js.map +1 -0
  235. package/dist/core/run/scope-hash.d.ts +71 -0
  236. package/dist/core/run/scope-hash.d.ts.map +1 -0
  237. package/dist/core/run/scope-hash.js +177 -0
  238. package/dist/core/run/scope-hash.js.map +1 -0
  239. package/dist/core/run/scope.d.ts +30 -0
  240. package/dist/core/run/scope.d.ts.map +1 -0
  241. package/dist/core/run/scope.js +193 -0
  242. package/dist/core/run/scope.js.map +1 -0
  243. package/dist/core/run/summary.d.ts +8 -0
  244. package/dist/core/run/summary.d.ts.map +1 -0
  245. package/dist/core/run/summary.js +25 -0
  246. package/dist/core/run/summary.js.map +1 -0
  247. package/dist/core/run/verification.d.ts +23 -0
  248. package/dist/core/run/verification.d.ts.map +1 -0
  249. package/dist/core/run/verification.js +78 -0
  250. package/dist/core/run/verification.js.map +1 -0
  251. package/dist/core/spec/approve.d.ts +31 -0
  252. package/dist/core/spec/approve.d.ts.map +1 -0
  253. package/dist/core/spec/approve.js +111 -0
  254. package/dist/core/spec/approve.js.map +1 -0
  255. package/dist/core/spec/delta.d.ts +77 -0
  256. package/dist/core/spec/delta.d.ts.map +1 -0
  257. package/dist/core/spec/delta.js +581 -0
  258. package/dist/core/spec/delta.js.map +1 -0
  259. package/dist/core/spec/digest-flags.d.ts +21 -0
  260. package/dist/core/spec/digest-flags.d.ts.map +1 -0
  261. package/dist/core/spec/digest-flags.js +194 -0
  262. package/dist/core/spec/digest-flags.js.map +1 -0
  263. package/dist/core/spec/digest-verify-starts.d.ts +5 -0
  264. package/dist/core/spec/digest-verify-starts.d.ts.map +1 -0
  265. package/dist/core/spec/digest-verify-starts.js +31 -0
  266. package/dist/core/spec/digest-verify-starts.js.map +1 -0
  267. package/dist/core/spec/digest.d.ts +42 -0
  268. package/dist/core/spec/digest.d.ts.map +1 -0
  269. package/dist/core/spec/digest.js +170 -0
  270. package/dist/core/spec/digest.js.map +1 -0
  271. package/dist/core/spec/hasher.d.ts +11 -0
  272. package/dist/core/spec/hasher.d.ts.map +1 -0
  273. package/dist/core/{hasher.js → spec/hasher.js} +27 -3
  274. package/dist/core/spec/hasher.js.map +1 -0
  275. package/dist/core/spec/linter.d.ts +79 -0
  276. package/dist/core/spec/linter.d.ts.map +1 -0
  277. package/dist/core/spec/linter.js +826 -0
  278. package/dist/core/spec/linter.js.map +1 -0
  279. package/dist/core/spec/migrate.d.ts +54 -0
  280. package/dist/core/spec/migrate.d.ts.map +1 -0
  281. package/dist/core/spec/migrate.js +297 -0
  282. package/dist/core/spec/migrate.js.map +1 -0
  283. package/dist/core/spec/openspec-version.d.ts +30 -0
  284. package/dist/core/spec/openspec-version.d.ts.map +1 -0
  285. package/dist/core/spec/openspec-version.js +118 -0
  286. package/dist/core/spec/openspec-version.js.map +1 -0
  287. package/dist/core/spec/parser.d.ts +82 -0
  288. package/dist/core/spec/parser.d.ts.map +1 -0
  289. package/dist/core/spec/parser.js +221 -0
  290. package/dist/core/spec/parser.js.map +1 -0
  291. package/dist/core/spec/verify-paths.d.ts +18 -0
  292. package/dist/core/spec/verify-paths.d.ts.map +1 -0
  293. package/dist/core/spec/verify-paths.js +139 -0
  294. package/dist/core/spec/verify-paths.js.map +1 -0
  295. package/dist/core/spec/verify-starts.d.ts +45 -0
  296. package/dist/core/spec/verify-starts.d.ts.map +1 -0
  297. package/dist/core/spec/verify-starts.js +61 -0
  298. package/dist/core/spec/verify-starts.js.map +1 -0
  299. package/dist/core/status/inbox-cursor.d.ts +10 -0
  300. package/dist/core/status/inbox-cursor.d.ts.map +1 -0
  301. package/dist/core/status/inbox-cursor.js +33 -0
  302. package/dist/core/status/inbox-cursor.js.map +1 -0
  303. package/dist/core/status/inbox-projection.d.ts +15 -0
  304. package/dist/core/status/inbox-projection.d.ts.map +1 -0
  305. package/dist/core/status/inbox-projection.js +20 -0
  306. package/dist/core/status/inbox-projection.js.map +1 -0
  307. package/dist/core/status/inbox-text.d.ts +4 -0
  308. package/dist/core/status/inbox-text.d.ts.map +1 -0
  309. package/dist/core/status/inbox-text.js +48 -0
  310. package/dist/core/status/inbox-text.js.map +1 -0
  311. package/dist/core/status/inbox.d.ts +53 -0
  312. package/dist/core/status/inbox.d.ts.map +1 -0
  313. package/dist/core/status/inbox.js +167 -0
  314. package/dist/core/status/inbox.js.map +1 -0
  315. package/dist/core/status/layout.d.ts +29 -0
  316. package/dist/core/status/layout.d.ts.map +1 -0
  317. package/dist/core/status/layout.js +77 -0
  318. package/dist/core/status/layout.js.map +1 -0
  319. package/dist/core/status/queue-parser.d.ts +13 -0
  320. package/dist/core/status/queue-parser.d.ts.map +1 -0
  321. package/dist/core/status/queue-parser.js +88 -0
  322. package/dist/core/status/queue-parser.js.map +1 -0
  323. package/dist/core/status/queue-planning.d.ts +62 -0
  324. package/dist/core/status/queue-planning.d.ts.map +1 -0
  325. package/dist/core/status/queue-planning.js +158 -0
  326. package/dist/core/status/queue-planning.js.map +1 -0
  327. package/dist/core/status/queue-report-detail.d.ts +50 -0
  328. package/dist/core/status/queue-report-detail.d.ts.map +1 -0
  329. package/dist/core/status/queue-report-detail.js +140 -0
  330. package/dist/core/status/queue-report-detail.js.map +1 -0
  331. package/dist/core/status/queue-report.d.ts +14 -0
  332. package/dist/core/status/queue-report.d.ts.map +1 -0
  333. package/dist/core/status/queue-report.js +41 -0
  334. package/dist/core/status/queue-report.js.map +1 -0
  335. package/dist/core/status/queue-state.d.ts +53 -0
  336. package/dist/core/status/queue-state.d.ts.map +1 -0
  337. package/dist/core/status/queue-state.js +167 -0
  338. package/dist/core/status/queue-state.js.map +1 -0
  339. package/dist/core/status/queue.d.ts +7 -0
  340. package/dist/core/status/queue.d.ts.map +1 -0
  341. package/dist/core/status/queue.js +4 -0
  342. package/dist/core/status/queue.js.map +1 -0
  343. package/dist/core/status/show.d.ts +102 -0
  344. package/dist/core/status/show.d.ts.map +1 -0
  345. package/dist/core/status/show.js +704 -0
  346. package/dist/core/status/show.js.map +1 -0
  347. package/dist/core/status/state.d.ts +56 -0
  348. package/dist/core/status/state.d.ts.map +1 -0
  349. package/dist/core/status/state.js +187 -0
  350. package/dist/core/status/state.js.map +1 -0
  351. package/dist/core/status/status.d.ts +23 -0
  352. package/dist/core/status/status.d.ts.map +1 -0
  353. package/dist/core/status/status.js +173 -0
  354. package/dist/core/status/status.js.map +1 -0
  355. package/dist/core/web/web-data-change.d.ts +10 -0
  356. package/dist/core/web/web-data-change.d.ts.map +1 -0
  357. package/dist/core/web/web-data-change.js +106 -0
  358. package/dist/core/web/web-data-change.js.map +1 -0
  359. package/dist/core/web/web-data-folders.d.ts +39 -0
  360. package/dist/core/web/web-data-folders.d.ts.map +1 -0
  361. package/dist/core/web/web-data-folders.js +125 -0
  362. package/dist/core/web/web-data-folders.js.map +1 -0
  363. package/dist/core/web/web-data-graph.d.ts +8 -0
  364. package/dist/core/web/web-data-graph.d.ts.map +1 -0
  365. package/dist/core/web/web-data-graph.js +113 -0
  366. package/dist/core/web/web-data-graph.js.map +1 -0
  367. package/dist/core/web/web-data-lifecycle.d.ts +43 -0
  368. package/dist/core/web/web-data-lifecycle.d.ts.map +1 -0
  369. package/dist/core/web/web-data-lifecycle.js +141 -0
  370. package/dist/core/web/web-data-lifecycle.js.map +1 -0
  371. package/dist/core/web/web-data-observations.d.ts +30 -0
  372. package/dist/core/web/web-data-observations.d.ts.map +1 -0
  373. package/dist/core/web/web-data-observations.js +181 -0
  374. package/dist/core/web/web-data-observations.js.map +1 -0
  375. package/dist/core/web/web-data-tokens.d.ts +29 -0
  376. package/dist/core/web/web-data-tokens.d.ts.map +1 -0
  377. package/dist/core/web/web-data-tokens.js +62 -0
  378. package/dist/core/web/web-data-tokens.js.map +1 -0
  379. package/dist/core/web/web-data-types.d.ts +156 -0
  380. package/dist/core/web/web-data-types.d.ts.map +1 -0
  381. package/dist/core/web/web-data-types.js +15 -0
  382. package/dist/core/web/web-data-types.js.map +1 -0
  383. package/dist/core/web/web-data.d.ts +10 -0
  384. package/dist/core/web/web-data.d.ts.map +1 -0
  385. package/dist/core/web/web-data.js +8 -0
  386. package/dist/core/web/web-data.js.map +1 -0
  387. package/dist/core/web/web-events.d.ts +54 -0
  388. package/dist/core/web/web-events.d.ts.map +1 -0
  389. package/dist/core/web/web-events.js +162 -0
  390. package/dist/core/web/web-events.js.map +1 -0
  391. package/dist/core/web/web-export.d.ts +18 -0
  392. package/dist/core/web/web-export.d.ts.map +1 -0
  393. package/dist/core/web/web-export.js +112 -0
  394. package/dist/core/web/web-export.js.map +1 -0
  395. package/dist/core/web/web-server.d.ts +34 -0
  396. package/dist/core/web/web-server.d.ts.map +1 -0
  397. package/dist/core/web/web-server.js +182 -0
  398. package/dist/core/web/web-server.js.map +1 -0
  399. package/dist/core/web/web-static.d.ts +20 -0
  400. package/dist/core/web/web-static.d.ts.map +1 -0
  401. package/dist/core/web/web-static.js +104 -0
  402. package/dist/core/web/web-static.js.map +1 -0
  403. package/dist/harness/{agy.d.ts → agy/agy.d.ts} +8 -6
  404. package/dist/harness/agy/agy.d.ts.map +1 -0
  405. package/dist/harness/{agy.js → agy/agy.js} +49 -34
  406. package/dist/harness/agy/agy.js.map +1 -0
  407. package/dist/harness/claude/claude-turns.d.ts +29 -0
  408. package/dist/harness/claude/claude-turns.d.ts.map +1 -0
  409. package/dist/harness/claude/claude-turns.js +162 -0
  410. package/dist/harness/claude/claude-turns.js.map +1 -0
  411. package/dist/harness/claude/claude-usage.d.ts +12 -0
  412. package/dist/harness/claude/claude-usage.d.ts.map +1 -0
  413. package/dist/harness/claude/claude-usage.js +144 -0
  414. package/dist/harness/claude/claude-usage.js.map +1 -0
  415. package/dist/harness/codex/codex-observe-usage.d.ts +6 -0
  416. package/dist/harness/codex/codex-observe-usage.d.ts.map +1 -0
  417. package/dist/harness/codex/codex-observe-usage.js +215 -0
  418. package/dist/harness/codex/codex-observe-usage.js.map +1 -0
  419. package/dist/harness/codex/codex-prompt.d.ts +17 -0
  420. package/dist/harness/codex/codex-prompt.d.ts.map +1 -0
  421. package/dist/harness/codex/codex-prompt.js +43 -0
  422. package/dist/harness/codex/codex-prompt.js.map +1 -0
  423. package/dist/harness/codex/codex-stream.d.ts +23 -0
  424. package/dist/harness/codex/codex-stream.d.ts.map +1 -0
  425. package/dist/harness/codex/codex-stream.js +182 -0
  426. package/dist/harness/codex/codex-stream.js.map +1 -0
  427. package/dist/harness/codex/codex-usage.d.ts +26 -0
  428. package/dist/harness/codex/codex-usage.d.ts.map +1 -0
  429. package/dist/harness/codex/codex-usage.js +122 -0
  430. package/dist/harness/codex/codex-usage.js.map +1 -0
  431. package/dist/harness/codex/codex.d.ts +16 -0
  432. package/dist/harness/codex/codex.d.ts.map +1 -0
  433. package/dist/harness/codex/codex.js +92 -0
  434. package/dist/harness/codex/codex.js.map +1 -0
  435. package/dist/harness/index.d.ts +11 -2
  436. package/dist/harness/index.d.ts.map +1 -1
  437. package/dist/harness/index.js +24 -9
  438. package/dist/harness/index.js.map +1 -1
  439. package/dist/harness/mock.d.ts +5 -2
  440. package/dist/harness/mock.d.ts.map +1 -1
  441. package/dist/harness/mock.js +10 -11
  442. package/dist/harness/mock.js.map +1 -1
  443. package/dist/harness/opencode/opencode-observe-usage.d.ts +4 -0
  444. package/dist/harness/opencode/opencode-observe-usage.d.ts.map +1 -0
  445. package/dist/harness/opencode/opencode-observe-usage.js +100 -0
  446. package/dist/harness/opencode/opencode-observe-usage.js.map +1 -0
  447. package/dist/harness/opencode/opencode-usage.d.ts +24 -0
  448. package/dist/harness/opencode/opencode-usage.d.ts.map +1 -0
  449. package/dist/harness/opencode/opencode-usage.js +181 -0
  450. package/dist/harness/opencode/opencode-usage.js.map +1 -0
  451. package/dist/harness/{opencode.d.ts → opencode/opencode.d.ts} +10 -7
  452. package/dist/harness/opencode/opencode.d.ts.map +1 -0
  453. package/dist/harness/{opencode.js → opencode/opencode.js} +159 -70
  454. package/dist/harness/opencode/opencode.js.map +1 -0
  455. package/dist/harness/pi/pi-args.d.ts +9 -0
  456. package/dist/harness/pi/pi-args.d.ts.map +1 -0
  457. package/dist/harness/pi/pi-args.js +33 -0
  458. package/dist/harness/pi/pi-args.js.map +1 -0
  459. package/dist/harness/pi/pi-stream.d.ts +27 -0
  460. package/dist/harness/pi/pi-stream.d.ts.map +1 -0
  461. package/dist/harness/pi/pi-stream.js +176 -0
  462. package/dist/harness/pi/pi-stream.js.map +1 -0
  463. package/dist/harness/pi/pi-tools.d.ts +5 -0
  464. package/dist/harness/pi/pi-tools.d.ts.map +1 -0
  465. package/dist/harness/pi/pi-tools.js +31 -0
  466. package/dist/harness/pi/pi-tools.js.map +1 -0
  467. package/dist/harness/pi/pi.d.ts +11 -0
  468. package/dist/harness/pi/pi.d.ts.map +1 -0
  469. package/dist/harness/pi/pi.js +109 -0
  470. package/dist/harness/pi/pi.js.map +1 -0
  471. package/dist/harness/prompt.d.ts +11 -0
  472. package/dist/harness/prompt.d.ts.map +1 -0
  473. package/dist/harness/prompt.js +105 -0
  474. package/dist/harness/prompt.js.map +1 -0
  475. package/dist/harness/types.d.ts +287 -8
  476. package/dist/harness/types.d.ts.map +1 -1
  477. package/dist/harness/types.js +125 -0
  478. package/dist/harness/types.js.map +1 -1
  479. package/dist/index.d.ts +13 -2
  480. package/dist/index.d.ts.map +1 -1
  481. package/dist/index.js +8 -1
  482. package/dist/index.js.map +1 -1
  483. package/dist/watcher/archive-verify.d.ts +9 -0
  484. package/dist/watcher/archive-verify.d.ts.map +1 -0
  485. package/dist/watcher/archive-verify.js +80 -0
  486. package/dist/watcher/archive-verify.js.map +1 -0
  487. package/dist/watcher/archiver.d.ts +14 -2
  488. package/dist/watcher/archiver.d.ts.map +1 -1
  489. package/dist/watcher/archiver.js +112 -33
  490. package/dist/watcher/archiver.js.map +1 -1
  491. package/dist/watcher/attempt.d.ts +19 -0
  492. package/dist/watcher/attempt.d.ts.map +1 -0
  493. package/dist/watcher/attempt.js +94 -0
  494. package/dist/watcher/attempt.js.map +1 -0
  495. package/dist/watcher/auto-retry.d.ts +11 -0
  496. package/dist/watcher/auto-retry.d.ts.map +1 -0
  497. package/dist/watcher/auto-retry.js +180 -0
  498. package/dist/watcher/auto-retry.js.map +1 -0
  499. package/dist/watcher/build.d.ts +24 -0
  500. package/dist/watcher/build.d.ts.map +1 -0
  501. package/dist/watcher/build.js +169 -0
  502. package/dist/watcher/build.js.map +1 -0
  503. package/dist/watcher/change-verify.d.ts +25 -0
  504. package/dist/watcher/change-verify.d.ts.map +1 -0
  505. package/dist/watcher/change-verify.js +57 -0
  506. package/dist/watcher/change-verify.js.map +1 -0
  507. package/dist/watcher/dev.d.ts +51 -0
  508. package/dist/watcher/dev.d.ts.map +1 -0
  509. package/dist/watcher/dev.js +124 -0
  510. package/dist/watcher/dev.js.map +1 -0
  511. package/dist/watcher/failure-reason.d.ts +7 -0
  512. package/dist/watcher/failure-reason.d.ts.map +1 -0
  513. package/dist/watcher/failure-reason.js +2 -0
  514. package/dist/watcher/failure-reason.js.map +1 -0
  515. package/dist/watcher/fingerprint.d.ts +19 -0
  516. package/dist/watcher/fingerprint.d.ts.map +1 -0
  517. package/dist/watcher/fingerprint.js +53 -0
  518. package/dist/watcher/fingerprint.js.map +1 -0
  519. package/dist/watcher/heartbeat.d.ts +33 -0
  520. package/dist/watcher/heartbeat.d.ts.map +1 -0
  521. package/dist/watcher/heartbeat.js +135 -0
  522. package/dist/watcher/heartbeat.js.map +1 -0
  523. package/dist/watcher/lock.d.ts +10 -0
  524. package/dist/watcher/lock.d.ts.map +1 -0
  525. package/dist/watcher/lock.js +14 -0
  526. package/dist/watcher/lock.js.map +1 -0
  527. package/dist/watcher/loop.d.ts +26 -6
  528. package/dist/watcher/loop.d.ts.map +1 -1
  529. package/dist/watcher/loop.js +154 -37
  530. package/dist/watcher/loop.js.map +1 -1
  531. package/dist/watcher/measures.d.ts +46 -0
  532. package/dist/watcher/measures.d.ts.map +1 -0
  533. package/dist/watcher/measures.js +203 -0
  534. package/dist/watcher/measures.js.map +1 -0
  535. package/dist/watcher/outcome.d.ts +32 -0
  536. package/dist/watcher/outcome.d.ts.map +1 -0
  537. package/dist/watcher/outcome.js +127 -0
  538. package/dist/watcher/outcome.js.map +1 -0
  539. package/dist/watcher/regression.d.ts +43 -0
  540. package/dist/watcher/regression.d.ts.map +1 -0
  541. package/dist/watcher/regression.js +175 -0
  542. package/dist/watcher/regression.js.map +1 -0
  543. package/dist/watcher/runner.d.ts +6 -68
  544. package/dist/watcher/runner.d.ts.map +1 -1
  545. package/dist/watcher/runner.js +74 -523
  546. package/dist/watcher/runner.js.map +1 -1
  547. package/dist/watcher/spawn.d.ts +48 -0
  548. package/dist/watcher/spawn.d.ts.map +1 -0
  549. package/dist/watcher/spawn.js +130 -0
  550. package/dist/watcher/spawn.js.map +1 -0
  551. package/dist/watcher/task-verify.d.ts +59 -0
  552. package/dist/watcher/task-verify.d.ts.map +1 -0
  553. package/dist/watcher/task-verify.js +108 -0
  554. package/dist/watcher/task-verify.js.map +1 -0
  555. package/dist/watcher/verify.d.ts +44 -0
  556. package/dist/watcher/verify.d.ts.map +1 -0
  557. package/dist/watcher/verify.js +156 -0
  558. package/dist/watcher/verify.js.map +1 -0
  559. package/package.json +21 -9
  560. package/templates/PLANNER.md +77 -0
  561. package/templates/openspec/config.yaml +24 -0
  562. package/templates/openspec/schemas/osq/README.md +30 -0
  563. package/templates/openspec/schemas/osq/schema.yaml +133 -0
  564. package/templates/openspec/schemas/osq/templates/proposal.md +66 -0
  565. package/templates/openspec/schemas/osq/templates/spec.md +13 -0
  566. package/templates/openspec/schemas/osq/templates/tasks.md +11 -0
  567. package/templates/proposal.md +66 -0
  568. package/templates/tasks.md +5 -0
  569. package/ui/dist/assets/index-CZYeQ8la.js +9 -0
  570. package/ui/dist/assets/index-D6zVSjO7.css +1 -0
  571. package/ui/dist/index.html +14 -0
  572. package/dist/core/approve.d.ts +0 -11
  573. package/dist/core/approve.d.ts.map +0 -1
  574. package/dist/core/approve.js +0 -52
  575. package/dist/core/approve.js.map +0 -1
  576. package/dist/core/config.d.ts.map +0 -1
  577. package/dist/core/config.js.map +0 -1
  578. package/dist/core/hasher.d.ts +0 -4
  579. package/dist/core/hasher.d.ts.map +0 -1
  580. package/dist/core/hasher.js.map +0 -1
  581. package/dist/core/init.d.ts +0 -12
  582. package/dist/core/init.d.ts.map +0 -1
  583. package/dist/core/init.js +0 -151
  584. package/dist/core/init.js.map +0 -1
  585. package/dist/core/linter.d.ts +0 -8
  586. package/dist/core/linter.d.ts.map +0 -1
  587. package/dist/core/linter.js +0 -100
  588. package/dist/core/linter.js.map +0 -1
  589. package/dist/core/lock.d.ts.map +0 -1
  590. package/dist/core/lock.js.map +0 -1
  591. package/dist/core/logger.d.ts.map +0 -1
  592. package/dist/core/logger.js.map +0 -1
  593. package/dist/core/new.d.ts.map +0 -1
  594. package/dist/core/new.js +0 -79
  595. package/dist/core/new.js.map +0 -1
  596. package/dist/core/parser.d.ts +0 -32
  597. package/dist/core/parser.d.ts.map +0 -1
  598. package/dist/core/parser.js +0 -87
  599. package/dist/core/parser.js.map +0 -1
  600. package/dist/core/report.d.ts +0 -54
  601. package/dist/core/report.d.ts.map +0 -1
  602. package/dist/core/report.js +0 -475
  603. package/dist/core/report.js.map +0 -1
  604. package/dist/core/show.d.ts +0 -50
  605. package/dist/core/show.d.ts.map +0 -1
  606. package/dist/core/show.js +0 -374
  607. package/dist/core/show.js.map +0 -1
  608. package/dist/core/state.d.ts +0 -25
  609. package/dist/core/state.d.ts.map +0 -1
  610. package/dist/core/state.js +0 -180
  611. package/dist/core/state.js.map +0 -1
  612. package/dist/core/status.d.ts +0 -10
  613. package/dist/core/status.d.ts.map +0 -1
  614. package/dist/core/status.js +0 -106
  615. package/dist/core/status.js.map +0 -1
  616. package/dist/harness/agy.d.ts.map +0 -1
  617. package/dist/harness/agy.js.map +0 -1
  618. package/dist/harness/opencode.d.ts.map +0 -1
  619. package/dist/harness/opencode.js.map +0 -1
@@ -0,0 +1,826 @@
1
+ import { execFile } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { resolveScope } from '../run/scope.js';
5
+ import { getArchiveDir, getChangesDir, getRejectedDir } from '../status/layout.js';
6
+ import { compareNumericPrefix } from '../status/state.js';
7
+ import { DeltaMergeError, mergeDelta, parseDelta } from './delta.js';
8
+ import { isExcludedChangePath } from './hasher.js';
9
+ import { OPENSPEC_EXPECTED_VERSION, assessOpenSpecVersion, formatInRangeWarning, } from './openspec-version.js';
10
+ import { hasDeclaredWrites, parseFrontmatter, parseSpecMd, parseTaskMd, resolveChangeDoc, } from './parser.js';
11
+ import { listNamedPaths, missingNamedPaths, namesExistingRepositoryPath, tokenizeVerifyCommand, } from './verify-paths.js';
12
+ import { analyzeVerifyStarts, namedPathCoveredByScopes, } from './verify-starts.js';
13
+ export { OPENSPEC_EXPECTED_VERSION };
14
+ /** Remediation guidance citing ADR 004 for any validator pin violation. */
15
+ const OPENSPEC_PIN_REMEDIATION = 'Refer to ADR 004 (decisions/004-pinned-openspec-validator.md) and run: pnpm add -D @fission-ai/openspec@1.13.1';
16
+ /** Prefix applied to every finding surfaced by the OpenSpec validator. */
17
+ export const OPENSPEC_ERROR_PREFIX = 'openspec:';
18
+ /**
19
+ * ASCII control characters prohibited in authored change-folder files. Newline
20
+ * (`\n`, 0x0A) and tab (`\t`, 0x09) are permitted as structural Markdown
21
+ * whitespace; every other control character indicates a mangled write.
22
+ */
23
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional control character detector
24
+ const PROHIBITED_CONTROL_REGEX = /[\x00-\x08\x0B-\x1F\x7F]/;
25
+ /** Acceptance checkbox pattern; two or more on one line indicates fused lines. */
26
+ const ACCEPTANCE_CHECKBOX_REGEX = /[-*]\s*\[[ xX]\]/g;
27
+ /**
28
+ * Requirement names that begin with an imperative instruction verb. A living
29
+ * capability spec declares observable state; a delta requirement whose name
30
+ * starts with "update" or "document" is an instruction and is rejected.
31
+ */
32
+ const INSTRUCTION_SHAPED_REQUIREMENT_REGEX = /^(?:update|document)/i;
33
+ /** The exact template verify sentinel planning must replace before approval. */
34
+ const PLACEHOLDER_VERIFY_COMMAND = 'node -e "process.exit(0)"';
35
+ /** The one find-it error for a missing or empty proposal `## Surface` section. */
36
+ const PROPOSAL_SURFACE_ERROR = 'proposal.md needs a ## Surface section: list the commands, flags, config keys, frontmatter fields, document sections, dead reasons, and event types this change adds, changes, or removes, or write None';
37
+ /** HTML comments are structural scaffolding, not declared surface text. */
38
+ const HTML_COMMENT_REGEX = /<!--[\s\S]*?-->/g;
39
+ /**
40
+ * Reads a proposal body's `## Surface` section content. The heading must occupy
41
+ * its own line; a mention inside prose does not count. Returns `null` when the
42
+ * section is absent.
43
+ */
44
+ function readSurfaceSection(body) {
45
+ const match = /^##\s+Surface\s*$/m.exec(body);
46
+ if (!match) {
47
+ return null;
48
+ }
49
+ const rest = body.slice(match.index + match[0].length);
50
+ const boundary = rest.search(/\n##(?!#)\s/);
51
+ return boundary === -1 ? rest : rest.slice(0, boundary);
52
+ }
53
+ /**
54
+ * True when a proposal has no `## Surface` section, or the section holds only
55
+ * HTML comments and whitespace. Lint checks presence, never the declared text.
56
+ */
57
+ function proposalSurfaceIsEmpty(body) {
58
+ const section = readSurfaceSection(body);
59
+ if (section === null) {
60
+ return true;
61
+ }
62
+ return section.replace(HTML_COMMENT_REGEX, '').trim().length === 0;
63
+ }
64
+ /** Package managers whose direct or `run` invocations reference a script. */
65
+ const PACKAGE_MANAGER_BINARIES = new Set(['pnpm', 'npm', 'yarn', 'bun']);
66
+ /** Package-script verbs that introduce the referenced script name. */
67
+ const PACKAGE_RUN_VERBS = new Set(['run', 'run-script']);
68
+ /**
69
+ * Package-manager subcommands that are not package scripts. Recognizing these
70
+ * as scripts would turn an ordinary package-manager command into a spurious
71
+ * missing-script error, so they fall through to the generic path analysis.
72
+ */
73
+ const PACKAGE_MANAGER_SUBCOMMANDS = new Set([
74
+ 'add',
75
+ 'audit',
76
+ 'bin',
77
+ 'cache',
78
+ 'config',
79
+ 'create',
80
+ 'dedupe',
81
+ 'dlx',
82
+ 'doctor',
83
+ 'env',
84
+ 'exec',
85
+ 'import',
86
+ 'init',
87
+ 'install',
88
+ 'licenses',
89
+ 'link',
90
+ 'list',
91
+ 'login',
92
+ 'logout',
93
+ 'ls',
94
+ 'outdated',
95
+ 'pack',
96
+ 'patch',
97
+ 'prune',
98
+ 'publish',
99
+ 'rebuild',
100
+ 'remove',
101
+ 'root',
102
+ 'setup',
103
+ 'store',
104
+ 'unlink',
105
+ 'uninstall',
106
+ 'update',
107
+ 'upgrade',
108
+ 'version',
109
+ 'whoami',
110
+ 'why',
111
+ 'workspace',
112
+ 'workspaces',
113
+ ]);
114
+ /**
115
+ * Recognize the template sentinel under the narrow normalizations the spec
116
+ * allows: surrounding and repeated ASCII whitespace, single or double quotes
117
+ * around the expression, `-e` or `--eval`, and an optional trailing semicolon.
118
+ */
119
+ function isPlaceholderVerify(tokens) {
120
+ if (tokens.length !== 3 || tokens[0] !== 'node') {
121
+ return false;
122
+ }
123
+ if (tokens[1] !== '-e' && tokens[1] !== '--eval') {
124
+ return false;
125
+ }
126
+ const expression = tokens[2].trim().replace(/;+$/, '').replace(/\s+/g, '');
127
+ return expression === 'process.exit(0)';
128
+ }
129
+ /** A shell chain operator; lint already rejects these for task verifies. */
130
+ function chainsVerifyCommand(command) {
131
+ return command.includes('&&') || command.includes(';') || command.includes('|');
132
+ }
133
+ /** Extract the referenced package script name from a recognized invocation. */
134
+ function extractPackageScript(tokens) {
135
+ const [bin, first, second] = tokens;
136
+ if (bin === undefined || !PACKAGE_MANAGER_BINARIES.has(bin)) {
137
+ return null;
138
+ }
139
+ if (first === undefined || first.startsWith('-')) {
140
+ return null;
141
+ }
142
+ if (PACKAGE_RUN_VERBS.has(first)) {
143
+ if (second === undefined || second.startsWith('-')) {
144
+ return null;
145
+ }
146
+ return second;
147
+ }
148
+ if (PACKAGE_MANAGER_SUBCOMMANDS.has(first)) {
149
+ return null;
150
+ }
151
+ return first;
152
+ }
153
+ /**
154
+ * Analyze one verify command read-only: detect the template sentinel, resolve a
155
+ * recognized package script against the root manifest, or fall back to an
156
+ * unresolved-target warning when no existing path or script is named.
157
+ */
158
+ export async function analyzeVerifyCommand(projectRoot, command, packageScripts) {
159
+ const tokens = tokenizeVerifyCommand(command);
160
+ if (isPlaceholderVerify(tokens)) {
161
+ return { placeholder: true, packageScript: null, missingScript: null, unresolved: false };
162
+ }
163
+ const packageScript = extractPackageScript(tokens);
164
+ if (packageScript !== null) {
165
+ return {
166
+ placeholder: false,
167
+ packageScript,
168
+ missingScript: packageScripts.has(packageScript) ? null : packageScript,
169
+ unresolved: false,
170
+ };
171
+ }
172
+ return {
173
+ placeholder: false,
174
+ packageScript: null,
175
+ missingScript: null,
176
+ unresolved: !(await namesExistingRepositoryPath(projectRoot, tokens)),
177
+ };
178
+ }
179
+ /**
180
+ * Read the root manifest's `scripts` keys once. A missing or malformed manifest
181
+ * deterministically yields an empty set rather than throwing, and the command
182
+ * itself is never executed.
183
+ */
184
+ async function readRootPackageScripts(projectRoot) {
185
+ try {
186
+ const raw = await fs.readFile(path.join(projectRoot, 'package.json'), 'utf8');
187
+ const parsed = JSON.parse(raw);
188
+ const scripts = parsed.scripts;
189
+ if (scripts !== null && typeof scripts === 'object' && !Array.isArray(scripts)) {
190
+ return new Set(Object.keys(scripts));
191
+ }
192
+ }
193
+ catch {
194
+ // Missing or malformed manifest: no known scripts.
195
+ }
196
+ return new Set();
197
+ }
198
+ function placeholderVerifyError(label) {
199
+ return `${label} verify is the template placeholder ${PLACEHOLDER_VERIFY_COMMAND}; replace it with a real command that verifies the completed change's final tree`;
200
+ }
201
+ function missingPackageScriptError(label, script) {
202
+ return `${label} verify references package script "${script}" that is not defined in the root package.json`;
203
+ }
204
+ function unresolvedVerifyWarning(label, command) {
205
+ return `${label} verify names neither an existing repository path nor a package script: "${command}"`;
206
+ }
207
+ /**
208
+ * Recursively list a change folder's authored files relative to `baseDir`,
209
+ * applying the shared root-relative exclusion predicate. `.run`, `.git`, and
210
+ * `.DS_Store` are excluded at any depth while the transient root
211
+ * `plan-prompt.md` is excluded without hiding a nested same-named authored file.
212
+ */
213
+ async function collectArtifactFiles(dir, baseDir) {
214
+ let entries;
215
+ try {
216
+ entries = await fs.readdir(dir, { withFileTypes: true });
217
+ }
218
+ catch {
219
+ return [];
220
+ }
221
+ const files = [];
222
+ for (const entry of entries) {
223
+ const fullPath = path.join(dir, entry.name);
224
+ const relPath = path.relative(baseDir, fullPath).split(path.sep).join('/');
225
+ if (isExcludedChangePath(relPath)) {
226
+ continue;
227
+ }
228
+ if (entry.isDirectory()) {
229
+ files.push(...(await collectArtifactFiles(fullPath, baseDir)));
230
+ }
231
+ else if (entry.isFile()) {
232
+ files.push(relPath);
233
+ }
234
+ }
235
+ return files;
236
+ }
237
+ /** Report the first prohibited control character found in `content`. */
238
+ function scanControlCharacters(relPath, content, errors) {
239
+ const match = PROHIBITED_CONTROL_REGEX.exec(content);
240
+ if (!match) {
241
+ return;
242
+ }
243
+ const code = match[0].charCodeAt(0).toString(16).toUpperCase().padStart(2, '0');
244
+ errors.push(`File ${relPath} contains prohibited control character (0x${code})`);
245
+ }
246
+ /** Report any task line carrying two or more acceptance checkbox items. */
247
+ function scanFusedAcceptance(taskFile, content, errors) {
248
+ const lines = content.split('\n');
249
+ lines.forEach((line, index) => {
250
+ const count = line.match(ACCEPTANCE_CHECKBOX_REGEX)?.length ?? 0;
251
+ if (count > 1) {
252
+ errors.push(`Task in ${taskFile} contains fused acceptance lines on line ${index + 1}`);
253
+ }
254
+ });
255
+ }
256
+ async function pathExists(target) {
257
+ return fs
258
+ .stat(target)
259
+ .then(() => true)
260
+ .catch(() => false);
261
+ }
262
+ function resolveOpenSpecSettings(config) {
263
+ const extended = config;
264
+ return extended.openspec ?? {};
265
+ }
266
+ function resolveOpenSpecRoot(config) {
267
+ const paths = config.paths;
268
+ return paths.openspecRoot ?? 'openspec';
269
+ }
270
+ /**
271
+ * Resolves the project-local OpenSpec binary. Only a binary inside the
272
+ * project's own `node_modules/.bin` (or an explicitly configured absolute
273
+ * path) is used; missing installs resolve to `null` so linting degrades
274
+ * gracefully rather than failing on projects without OpenSpec.
275
+ */
276
+ export async function resolveOpenSpecBin(projectRoot, config) {
277
+ const binName = resolveOpenSpecSettings(config).bin?.trim() || 'openspec';
278
+ if (path.isAbsolute(binName) || binName.includes('/') || binName.includes('\\')) {
279
+ return (await pathExists(binName)) ? binName : null;
280
+ }
281
+ const localBin = path.join(projectRoot, 'node_modules', '.bin', binName);
282
+ return (await pathExists(localBin)) ? localBin : null;
283
+ }
284
+ /** Reads the resolved `@fission-ai/openspec` version from the local install. */
285
+ export async function resolveOpenSpecVersion(projectRoot) {
286
+ const manifestPath = path.join(projectRoot, 'node_modules', '@fission-ai', 'openspec', 'package.json');
287
+ try {
288
+ const raw = await fs.readFile(manifestPath, 'utf8');
289
+ const manifest = JSON.parse(raw);
290
+ return manifest.version ?? null;
291
+ }
292
+ catch {
293
+ return null;
294
+ }
295
+ }
296
+ function messageOf(value) {
297
+ if (typeof value === 'string') {
298
+ return value;
299
+ }
300
+ if (!value || typeof value !== 'object') {
301
+ return null;
302
+ }
303
+ const record = value;
304
+ for (const key of ['message', 'error', 'description', 'title', 'label', 'detail']) {
305
+ const candidate = record[key];
306
+ if (typeof candidate === 'string' && candidate.trim()) {
307
+ return candidate;
308
+ }
309
+ }
310
+ return null;
311
+ }
312
+ function levelOf(value) {
313
+ if (value && typeof value === 'object') {
314
+ const record = value;
315
+ for (const key of ['severity', 'level', 'type', 'kind']) {
316
+ const candidate = record[key];
317
+ if (typeof candidate === 'string') {
318
+ const lowered = candidate.toLowerCase();
319
+ if (lowered.includes('warn') || lowered.includes('info')) {
320
+ return 'warning';
321
+ }
322
+ if (lowered.includes('error') || lowered.includes('fail')) {
323
+ return 'error';
324
+ }
325
+ }
326
+ }
327
+ }
328
+ return 'error';
329
+ }
330
+ const FINDING_CONTAINERS = [
331
+ ['errors', 'error'],
332
+ ['warnings', 'warning'],
333
+ ['issues', undefined],
334
+ ['findings', undefined],
335
+ ['problems', undefined],
336
+ ['validations', undefined],
337
+ ['results', undefined],
338
+ ['items', undefined],
339
+ ];
340
+ /**
341
+ * Tolerantly extracts validation findings from `openspec validate --json`.
342
+ * Accepts a top-level array or an object exposing `issues`/`errors`/`warnings`
343
+ * (or similar) and classifies each finding by its error/warning severity.
344
+ */
345
+ export function parseOpenSpecFindings(output) {
346
+ const errors = [];
347
+ const warnings = [];
348
+ const trimmed = output.trim();
349
+ if (!trimmed) {
350
+ return { errors, warnings };
351
+ }
352
+ let parsed;
353
+ try {
354
+ parsed = JSON.parse(trimmed);
355
+ }
356
+ catch {
357
+ return { errors, warnings };
358
+ }
359
+ const seen = new Set();
360
+ const push = (level, message) => {
361
+ const normalized = message.trim();
362
+ if (!normalized) {
363
+ return;
364
+ }
365
+ const key = `${level}:${normalized}`;
366
+ if (seen.has(key)) {
367
+ return;
368
+ }
369
+ seen.add(key);
370
+ (level === 'warning' ? warnings : errors).push(normalized);
371
+ };
372
+ const visit = (value, hint) => {
373
+ if (typeof value === 'string') {
374
+ push(hint ?? 'error', value);
375
+ return;
376
+ }
377
+ if (Array.isArray(value)) {
378
+ for (const item of value) {
379
+ visit(item, hint);
380
+ }
381
+ return;
382
+ }
383
+ if (!value || typeof value !== 'object') {
384
+ return;
385
+ }
386
+ const record = value;
387
+ const message = messageOf(record);
388
+ if (message) {
389
+ push(hint ?? levelOf(record), message);
390
+ }
391
+ for (const [key, keyHint] of FINDING_CONTAINERS) {
392
+ if (key in record) {
393
+ visit(record[key], hint ?? keyHint);
394
+ }
395
+ }
396
+ };
397
+ visit(parsed);
398
+ return { errors, warnings };
399
+ }
400
+ function execFileCapture(bin, args, options) {
401
+ return new Promise((resolve) => {
402
+ execFile(bin, args, options, (error, stdout, stderr) => {
403
+ const code = error
404
+ ? typeof error.code === 'number'
405
+ ? error.code
406
+ : 1
407
+ : 0;
408
+ resolve({ stdout: stdout ?? '', stderr: stderr ?? '', code });
409
+ });
410
+ });
411
+ }
412
+ /**
413
+ * Maps a validator version assessment to the single finding it produces: none
414
+ * for the pin, one warning inside the declared peer range, and one error citing
415
+ * ADR 004 and the install command outside it.
416
+ */
417
+ function versionFinding(assessment) {
418
+ if (assessment.status === 'in-range' && assessment.range !== null) {
419
+ return { warning: formatInRangeWarning(assessment.version, assessment.range) };
420
+ }
421
+ if (assessment.status === 'out-of-range') {
422
+ return {
423
+ error: `OpenSpec validator version ${assessment.version} differs from pinned ${OPENSPEC_EXPECTED_VERSION}. ${OPENSPEC_PIN_REMEDIATION}`,
424
+ };
425
+ }
426
+ return {};
427
+ }
428
+ /**
429
+ * Runs the local OpenSpec validator for changes and specs. Validation failures
430
+ * are returned prefixed with `openspec:`. A missing binary or a version outside
431
+ * the declared peer range is an error that cites ADR 004 and the install
432
+ * command; a version inside the range that differs from the pin warns and cites
433
+ * ADR 005. Linting fails closed rather than silently degrading.
434
+ */
435
+ export async function validateWithOpenSpec(projectRoot, config, options = {}) {
436
+ const logger = options.logger;
437
+ const bin = await resolveOpenSpecBin(projectRoot, config);
438
+ if (!bin) {
439
+ return {
440
+ errors: [
441
+ `OpenSpec validator binary is unavailable at node_modules/.bin/openspec. ${OPENSPEC_PIN_REMEDIATION}`,
442
+ ],
443
+ warnings: [],
444
+ ran: false,
445
+ bin: null,
446
+ version: null,
447
+ };
448
+ }
449
+ const version = await resolveOpenSpecVersion(projectRoot);
450
+ const errors = [];
451
+ const warnings = [];
452
+ if (!version) {
453
+ errors.push(`OpenSpec validator version could not be resolved from node_modules/@fission-ai/openspec/package.json. ${OPENSPEC_PIN_REMEDIATION}`);
454
+ }
455
+ else {
456
+ logger?.info(`openspec version ${version}`);
457
+ const finding = versionFinding(await assessOpenSpecVersion(version));
458
+ if (finding.error !== undefined) {
459
+ errors.push(finding.error);
460
+ }
461
+ if (finding.warning !== undefined) {
462
+ warnings.push(finding.warning);
463
+ }
464
+ }
465
+ const timeout = config.timeouts.verifyTimeoutSeconds * 1000;
466
+ const commands = [
467
+ ['validate', '--changes', '--strict', '--json', '--no-interactive'],
468
+ ['validate', '--specs', '--strict', '--json', '--no-interactive'],
469
+ ];
470
+ for (const args of commands) {
471
+ const outcome = await execFileCapture(bin, args, {
472
+ cwd: projectRoot,
473
+ env: { ...process.env, OPENSPEC_TELEMETRY: '0' },
474
+ timeout,
475
+ });
476
+ const findings = parseOpenSpecFindings(outcome.stdout);
477
+ for (const error of findings.errors) {
478
+ errors.push(`${OPENSPEC_ERROR_PREFIX} ${error}`);
479
+ }
480
+ for (const warning of findings.warnings) {
481
+ warnings.push(`${OPENSPEC_ERROR_PREFIX} ${warning}`);
482
+ }
483
+ if (outcome.code !== 0 && findings.errors.length === 0 && findings.warnings.length === 0) {
484
+ const detail = (outcome.stderr || outcome.stdout).trim().split('\n')[0];
485
+ errors.push(`${OPENSPEC_ERROR_PREFIX} ${args.join(' ')} exited with code ${outcome.code}${detail ? `: ${detail}` : ''}`);
486
+ }
487
+ }
488
+ return { errors, warnings, ran: true, bin, version };
489
+ }
490
+ async function checkDependencyExists(projectRoot, depId, config) {
491
+ const dirsToCheck = [
492
+ getChangesDir(config.paths.openspecRoot, projectRoot),
493
+ getArchiveDir(config.paths.openspecRoot, projectRoot),
494
+ // A rejected change is a historical change: the reference stays auditable
495
+ // even though it never satisfies dependency completion.
496
+ getRejectedDir(config.paths.openspecRoot, projectRoot),
497
+ ];
498
+ const paddedDep = depId.padStart(3, '0');
499
+ for (const dir of dirsToCheck) {
500
+ try {
501
+ const entries = await fs.readdir(dir);
502
+ for (const entry of entries) {
503
+ if (entry.startsWith(`${paddedDep}-`) || entry === paddedDep) {
504
+ return true;
505
+ }
506
+ }
507
+ }
508
+ catch {
509
+ // Directory may not exist yet
510
+ }
511
+ }
512
+ return false;
513
+ }
514
+ /** Returns the leading instruction verb in a requirement name, else null. */
515
+ function instructionVerbOf(name) {
516
+ const match = name.trim().match(INSTRUCTION_SHAPED_REQUIREMENT_REGEX);
517
+ return match ? match[0].toLowerCase() : null;
518
+ }
519
+ /**
520
+ * Reports delta requirements whose names are instruction-shaped. Deltas must
521
+ * define living capability requirements, so names starting with an imperative
522
+ * verb such as "update" or "document" are rejected with the capability and the
523
+ * offending requirement title.
524
+ */
525
+ function scanInstructionShapedRequirements(capability, requirements, errors) {
526
+ for (const requirement of requirements) {
527
+ const verb = instructionVerbOf(requirement.name);
528
+ if (!verb) {
529
+ continue;
530
+ }
531
+ errors.push(`${OPENSPEC_ERROR_PREFIX} ${capability}: requirement "${requirement.name}" is instruction-shaped ("${verb}"); delta specs must define living capability requirements, not instructions`);
532
+ }
533
+ }
534
+ /**
535
+ * Verifies that every delta operation targeting a requirement resolves against
536
+ * the living base spec at `openspec/specs/<capability>/spec.md`. Applying the
537
+ * delta through the deterministic merge engine reports unmatched MODIFIED,
538
+ * REMOVED, or RENAMED targets; ADDED-only deltas may create a new capability.
539
+ */
540
+ export async function verifyDeltaTargets(projectRoot, folderPath, config) {
541
+ const errors = [];
542
+ const deltasDir = path.join(folderPath, 'specs');
543
+ let entries = [];
544
+ try {
545
+ entries = await fs.readdir(deltasDir, { withFileTypes: true });
546
+ }
547
+ catch {
548
+ return errors;
549
+ }
550
+ const capabilities = entries
551
+ .filter((entry) => entry.isDirectory())
552
+ .map((entry) => entry.name)
553
+ .sort();
554
+ if (capabilities.length === 0) {
555
+ return errors;
556
+ }
557
+ const specsRoot = path.join(projectRoot, resolveOpenSpecRoot(config), 'specs');
558
+ for (const capability of capabilities) {
559
+ const deltaContent = await fs
560
+ .readFile(path.join(deltasDir, capability, 'spec.md'), 'utf8')
561
+ .catch(() => null);
562
+ if (deltaContent === null) {
563
+ continue;
564
+ }
565
+ const baseContent = await fs
566
+ .readFile(path.join(specsRoot, capability, 'spec.md'), 'utf8')
567
+ .catch(() => null);
568
+ const delta = parseDelta(deltaContent);
569
+ scanInstructionShapedRequirements(capability, [...delta.added, ...delta.modified, ...delta.removed], errors);
570
+ try {
571
+ mergeDelta(baseContent, capability, delta);
572
+ }
573
+ catch (error) {
574
+ if (error instanceof DeltaMergeError) {
575
+ errors.push(`${OPENSPEC_ERROR_PREFIX} ${capability}: ${error.message}`);
576
+ }
577
+ else {
578
+ throw error;
579
+ }
580
+ }
581
+ }
582
+ return errors;
583
+ }
584
+ /**
585
+ * Compares each pair of tasks' resolved existing files and returns one
586
+ * deterministic warning per shared task-pair/file. Tasks are ordered by numeric
587
+ * filename with the established stable fallback, and paths within a pair are
588
+ * sorted, so directory enumeration order never affects the diagnostics.
589
+ * Declarations that resolve to no existing file are already excluded from
590
+ * `existingPaths`; the shared resolver deduplicated them per task, so a task
591
+ * never warns against itself.
592
+ */
593
+ function collectOverlapWarnings(scopes) {
594
+ const ordered = [...scopes].sort((a, b) => compareNumericPrefix(a.taskFile, b.taskFile));
595
+ const warnings = [];
596
+ for (let left = 0; left < ordered.length; left += 1) {
597
+ for (let right = left + 1; right < ordered.length; right += 1) {
598
+ const other = new Set(ordered[right].existingPaths);
599
+ const shared = ordered[left].existingPaths
600
+ .filter((relativePath) => other.has(relativePath))
601
+ .sort();
602
+ for (const relativePath of shared) {
603
+ warnings.push(`Tasks ${ordered[left].taskNumber} and ${ordered[right].taskNumber} share resolved scope file ${relativePath}`);
604
+ }
605
+ }
606
+ }
607
+ return warnings;
608
+ }
609
+ /**
610
+ * Warns once per task whose resolved scope contains a `src/harness/` file but
611
+ * no resolved file under `tests/fixtures/events/`. Reuses the resolver
612
+ * projection already collected for overlap analysis instead of re-walking.
613
+ */
614
+ function collectHarnessFixtureWarnings(scopes) {
615
+ const ordered = [...scopes].sort((a, b) => compareNumericPrefix(a.taskFile, b.taskFile));
616
+ const warnings = [];
617
+ for (const scope of ordered) {
618
+ const hasHarness = scope.existingPaths.some((entry) => entry.startsWith('src/harness/'));
619
+ const hasFixtures = scope.existingPaths.some((entry) => entry === 'tests/fixtures/events' || entry.startsWith('tests/fixtures/events/'));
620
+ if (hasHarness && !hasFixtures) {
621
+ warnings.push(`Task in ${scope.taskFile} resolves src/harness/ scope but no file under tests/fixtures/events/`);
622
+ }
623
+ }
624
+ return warnings;
625
+ }
626
+ /** Test files governed by the `tests.modify` gate; mirrors the `tests/**` default. */
627
+ function isTestFilePath(relativePath) {
628
+ return relativePath === 'tests' || relativePath.startsWith('tests/');
629
+ }
630
+ /**
631
+ * Reads `tests.modify` from raw task frontmatter so a non-boolean value can be
632
+ * rejected. `parseTaskMd` intentionally collapses invalid types to `false`;
633
+ * schema validation therefore lives here. The nested `tests: { modify: ... }`
634
+ * form takes precedence over the flat `tests.modify` key.
635
+ */
636
+ function readTestsModifyDeclaration(content) {
637
+ const { data } = parseFrontmatter(content);
638
+ const nested = data.tests;
639
+ if (nested !== null && typeof nested === 'object' && !Array.isArray(nested)) {
640
+ const record = nested;
641
+ if ('modify' in record) {
642
+ const value = record.modify;
643
+ return { present: true, valid: typeof value === 'boolean', value };
644
+ }
645
+ }
646
+ if ('tests.modify' in data) {
647
+ const value = data['tests.modify'];
648
+ return { present: true, valid: typeof value === 'boolean', value };
649
+ }
650
+ return { present: false, valid: true };
651
+ }
652
+ export async function lintChangeFolder(projectRoot, folderPath, config, options = {}) {
653
+ const errors = [];
654
+ const warnings = [];
655
+ const packageScripts = await readRootPackageScripts(projectRoot);
656
+ // Check: no authored change-folder file contains prohibited control characters
657
+ for (const relPath of await collectArtifactFiles(folderPath, folderPath)) {
658
+ const content = await fs.readFile(path.join(folderPath, relPath), 'utf8');
659
+ scanControlCharacters(relPath, content, errors);
660
+ }
661
+ const resolvedDoc = await resolveChangeDoc(folderPath);
662
+ if (!resolvedDoc) {
663
+ errors.push('proposal.md (or legacy spec.md) not found in change folder');
664
+ return { valid: false, errors, warnings };
665
+ }
666
+ const specContent = await fs.readFile(resolvedDoc.path, 'utf8');
667
+ const spec = parseSpecMd(specContent);
668
+ // Check: proposals must declare a change-level verify command
669
+ if (resolvedDoc.kind === 'proposal' && !spec.verify) {
670
+ errors.push('proposal.md must declare a verify command in frontmatter');
671
+ }
672
+ // Check: proposals must declare a non-empty ## Surface section; legacy spec.md
673
+ // change documents are exempt exactly as they are for the verify check.
674
+ if (resolvedDoc.kind === 'proposal' &&
675
+ proposalSurfaceIsEmpty(parseFrontmatter(specContent).body)) {
676
+ errors.push(PROPOSAL_SURFACE_ERROR);
677
+ }
678
+ // Check: the change-level verify participates in the same trust analysis
679
+ if (spec.verify) {
680
+ const docLabel = path.basename(resolvedDoc.path);
681
+ const analysis = await analyzeVerifyCommand(projectRoot, spec.verify, packageScripts);
682
+ if (analysis.placeholder) {
683
+ errors.push(placeholderVerifyError(docLabel));
684
+ }
685
+ else if (!chainsVerifyCommand(spec.verify)) {
686
+ if (analysis.missingScript !== null) {
687
+ errors.push(missingPackageScriptError(docLabel, analysis.missingScript));
688
+ }
689
+ else if (analysis.unresolved) {
690
+ warnings.push(unresolvedVerifyWarning(docLabel, spec.verify));
691
+ }
692
+ }
693
+ }
694
+ // Check: features.writes is retired; delta specs are the sole writes declaration
695
+ if (hasDeclaredWrites(parseFrontmatter(specContent).data)) {
696
+ errors.push('features.writes is no longer supported in proposal frontmatter; write declarations are derived strictly from delta specs under specs/<capability>/spec.md');
697
+ }
698
+ // Check: contract tables count
699
+ if (spec.contractTablesCount > config.limits.maxContractTables) {
700
+ errors.push(`Contract has ${spec.contractTablesCount} tables (max allowed is ${config.limits.maxContractTables})`);
701
+ }
702
+ // Check: depends_on exists
703
+ for (const dep of spec.dependsOn) {
704
+ const exists = await checkDependencyExists(projectRoot, dep, config);
705
+ if (!exists) {
706
+ errors.push(`depends_on names missing change: ${dep}`);
707
+ }
708
+ }
709
+ // Tasks checks
710
+ const tasksDir = path.join(folderPath, 'tasks');
711
+ let taskEntries = [];
712
+ try {
713
+ taskEntries = (await fs.readdir(tasksDir)).filter((e) => e.endsWith('.md'));
714
+ }
715
+ catch {
716
+ errors.push('tasks directory not found in change folder');
717
+ }
718
+ if (taskEntries.length === 0) {
719
+ errors.push('No task files found under tasks/');
720
+ }
721
+ const resolvedTaskScopes = [];
722
+ for (const taskFile of taskEntries) {
723
+ const taskPath = path.join(tasksDir, taskFile);
724
+ const taskContent = await fs.readFile(taskPath, 'utf8');
725
+ const task = parseTaskMd(taskContent);
726
+ // Check: acceptance items must each occupy their own line
727
+ scanFusedAcceptance(taskFile, taskContent, errors);
728
+ // Check: tests.modify, when declared, must be a boolean
729
+ const testsModify = readTestsModifyDeclaration(taskContent);
730
+ if (testsModify.present && !testsModify.valid) {
731
+ errors.push(`Task in ${taskFile} tests.modify must be a boolean (got ${JSON.stringify(testsModify.value)})`);
732
+ }
733
+ // Resolve the declared scope once through the shared deterministic resolver.
734
+ const resolved = task.scope.length > 0 ? await resolveScope(projectRoot, task.scope) : [];
735
+ const existingPaths = resolved
736
+ .filter((entry) => entry.absolutePath !== null)
737
+ .map((entry) => entry.relativePath);
738
+ const taskLabel = `Task in ${taskFile}`;
739
+ const analysis = task.verify
740
+ ? await analyzeVerifyCommand(projectRoot, task.verify, packageScripts)
741
+ : null;
742
+ resolvedTaskScopes.push({
743
+ taskFile,
744
+ taskNumber: taskFile.replace(/\.md$/, ''),
745
+ existingPaths,
746
+ task,
747
+ verifyAnalysis: analysis,
748
+ });
749
+ // Check: touching an existing test file requires tests.modify: true
750
+ if (!task.testsModify && existingPaths.length > 0) {
751
+ const touched = existingPaths.filter(isTestFilePath).sort();
752
+ if (touched.length > 0) {
753
+ errors.push(`Task in ${taskFile} scope touches existing test files (${touched.join(', ')}) without tests.modify: true`);
754
+ }
755
+ }
756
+ // Check: verify command empty, placeholder, chained, or missing script
757
+ if (analysis === null) {
758
+ errors.push(`Task in ${taskFile} verify command is empty`);
759
+ }
760
+ else if (analysis.placeholder) {
761
+ errors.push(placeholderVerifyError(taskLabel));
762
+ }
763
+ else if (chainsVerifyCommand(task.verify)) {
764
+ errors.push(`Task in ${taskFile} verify chains commands ("${task.verify}")`);
765
+ }
766
+ else if (analysis.missingScript !== null) {
767
+ errors.push(missingPackageScriptError(taskLabel, analysis.missingScript));
768
+ }
769
+ // Check: acceptance checklist length
770
+ if (task.acceptance.length > config.limits.maxAcceptanceLines) {
771
+ errors.push(`Task in ${taskFile} acceptance lines (${task.acceptance.length}) exceeds limit ${config.limits.maxAcceptanceLines}`);
772
+ }
773
+ // Check: scope expands to more than maxScopeFiles
774
+ if (task.scope.length > config.limits.maxScopeFiles) {
775
+ errors.push(`Task in ${taskFile} scope specifies ${task.scope.length} patterns, exceeding limit ${config.limits.maxScopeFiles}`);
776
+ }
777
+ }
778
+ // Check: harness scope should cover the event fixtures without failing lint
779
+ warnings.push(...collectHarnessFixtureWarnings(resolvedTaskScopes));
780
+ // Check: shared resolved files across tasks are reported as non-failing warnings
781
+ warnings.push(...collectOverlapWarnings(resolvedTaskScopes));
782
+ // Tasks are analyzed in numeric order so earlier scopes can cover a later
783
+ // task's new paths. The unresolved-target warning is suppressed when every
784
+ // missing named path is one the task's own or an earlier task's scope covers.
785
+ const orderedScopes = [...resolvedTaskScopes].sort((a, b) => compareNumericPrefix(a.taskFile, b.taskFile));
786
+ const earlierScopes = [];
787
+ for (const scope of orderedScopes) {
788
+ if (scope.verifyAnalysis?.unresolved && !chainsVerifyCommand(scope.task.verify)) {
789
+ const namedPaths = listNamedPaths(scope.task.verify);
790
+ const missingPaths = await missingNamedPaths(projectRoot, scope.task.verify);
791
+ const covered = namedPaths.length > 0 &&
792
+ missingPaths.every((namedPath) => namedPathCoveredByScopes(namedPath, [scope.task.scope, ...earlierScopes]));
793
+ if (!covered) {
794
+ warnings.push(unresolvedVerifyWarning(`Task in ${scope.taskFile}`, scope.task.verify));
795
+ }
796
+ }
797
+ earlierScopes.push([...scope.task.scope]);
798
+ }
799
+ // Check: a task whose verify names a test it creates must declare red, and a
800
+ // named path no task scope up to this task covers cannot be created at all
801
+ const verifyStartTasks = orderedScopes.map((scope) => ({
802
+ taskNumber: scope.taskNumber,
803
+ verify: scope.task.verify,
804
+ verifyStarts: scope.task.verifyStarts,
805
+ scope: scope.task.scope,
806
+ }));
807
+ const verifyStarts = await analyzeVerifyStarts(projectRoot, verifyStartTasks);
808
+ for (const contradiction of verifyStarts.contradictions) {
809
+ warnings.push(`Task in ${contradiction.taskNumber}.md declares verify_starts: ${contradiction.start} but its verify names ${contradiction.path}, which the task creates`);
810
+ }
811
+ for (const uncreatable of verifyStarts.uncreatable) {
812
+ warnings.push(`Task in ${uncreatable.taskNumber}.md verify names ${uncreatable.path}, which no task in the change can create`);
813
+ }
814
+ // Check: delta targets resolve against living base specs before approval
815
+ errors.push(...(await verifyDeltaTargets(projectRoot, folderPath, config)));
816
+ // Check: pinned OpenSpec validator
817
+ const openSpec = await validateWithOpenSpec(projectRoot, config, options);
818
+ errors.push(...openSpec.errors);
819
+ warnings.push(...openSpec.warnings);
820
+ return {
821
+ valid: errors.length === 0,
822
+ errors,
823
+ warnings,
824
+ };
825
+ }
826
+ //# sourceMappingURL=linter.js.map