@matteeh/osq 0.2.1 → 0.2.2

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 (606) hide show
  1. package/README.md +234 -15
  2. package/dist/cli/approve.d.ts +7 -0
  3. package/dist/cli/approve.d.ts.map +1 -1
  4. package/dist/cli/approve.js +34 -0
  5. package/dist/cli/approve.js.map +1 -1
  6. package/dist/cli/bin.js +2 -3
  7. package/dist/cli/bin.js.map +1 -1
  8. package/dist/cli/check.d.ts +15 -0
  9. package/dist/cli/check.d.ts.map +1 -0
  10. package/dist/cli/check.js +65 -0
  11. package/dist/cli/check.js.map +1 -0
  12. package/dist/cli/index.d.ts.map +1 -1
  13. package/dist/cli/index.js +10 -10
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/cli/init.d.ts.map +1 -1
  16. package/dist/cli/init.js +6 -1
  17. package/dist/cli/init.js.map +1 -1
  18. package/dist/cli/lint.d.ts +3 -0
  19. package/dist/cli/lint.d.ts.map +1 -1
  20. package/dist/cli/lint.js +33 -14
  21. package/dist/cli/lint.js.map +1 -1
  22. package/dist/cli/plan-queue.d.ts +10 -4
  23. package/dist/cli/plan-queue.d.ts.map +1 -1
  24. package/dist/cli/plan-queue.js +20 -19
  25. package/dist/cli/plan-queue.js.map +1 -1
  26. package/dist/cli/plan-sections.d.ts +15 -0
  27. package/dist/cli/plan-sections.d.ts.map +1 -0
  28. package/dist/cli/plan-sections.js +47 -0
  29. package/dist/cli/plan-sections.js.map +1 -0
  30. package/dist/cli/plan.d.ts +1 -0
  31. package/dist/cli/plan.d.ts.map +1 -1
  32. package/dist/cli/plan.js +14 -4
  33. package/dist/cli/plan.js.map +1 -1
  34. package/dist/cli/report.d.ts.map +1 -1
  35. package/dist/cli/report.js +73 -1
  36. package/dist/cli/report.js.map +1 -1
  37. package/dist/cli/run.d.ts +7 -0
  38. package/dist/cli/run.d.ts.map +1 -0
  39. package/dist/cli/run.js +22 -0
  40. package/dist/cli/run.js.map +1 -0
  41. package/dist/cli/verified.d.ts +15 -0
  42. package/dist/cli/verified.d.ts.map +1 -0
  43. package/dist/cli/verified.js +34 -0
  44. package/dist/cli/verified.js.map +1 -0
  45. package/dist/core/foundation/config-agents.d.ts +16 -0
  46. package/dist/core/foundation/config-agents.d.ts.map +1 -0
  47. package/dist/core/foundation/config-agents.js +2 -0
  48. package/dist/core/foundation/config-agents.js.map +1 -0
  49. package/dist/core/foundation/config-claude.d.ts +48 -0
  50. package/dist/core/foundation/config-claude.d.ts.map +1 -0
  51. package/dist/core/foundation/config-claude.js +106 -0
  52. package/dist/core/foundation/config-claude.js.map +1 -0
  53. package/dist/core/foundation/config-codex-resolve.d.ts +10 -0
  54. package/dist/core/foundation/config-codex-resolve.d.ts.map +1 -0
  55. package/dist/core/foundation/config-codex-resolve.js +24 -0
  56. package/dist/core/foundation/config-codex-resolve.js.map +1 -0
  57. package/dist/core/foundation/config-file.d.ts +18 -0
  58. package/dist/core/foundation/config-file.d.ts.map +1 -0
  59. package/dist/core/foundation/config-file.js +46 -0
  60. package/dist/core/foundation/config-file.js.map +1 -0
  61. package/dist/core/foundation/config-gates.d.ts +2 -0
  62. package/dist/core/foundation/config-gates.d.ts.map +1 -1
  63. package/dist/core/foundation/config-gates.js +8 -0
  64. package/dist/core/foundation/config-gates.js.map +1 -1
  65. package/dist/core/foundation/config-opencode.d.ts +25 -0
  66. package/dist/core/foundation/config-opencode.d.ts.map +1 -0
  67. package/dist/core/foundation/config-opencode.js +55 -0
  68. package/dist/core/foundation/config-opencode.js.map +1 -0
  69. package/dist/core/foundation/config-pi.d.ts +10 -0
  70. package/dist/core/foundation/config-pi.d.ts.map +1 -1
  71. package/dist/core/foundation/config-pi.js +13 -0
  72. package/dist/core/foundation/config-pi.js.map +1 -1
  73. package/dist/core/foundation/config-planning.d.ts +12 -1
  74. package/dist/core/foundation/config-planning.d.ts.map +1 -1
  75. package/dist/core/foundation/config-planning.js +29 -1
  76. package/dist/core/foundation/config-planning.js.map +1 -1
  77. package/dist/core/foundation/config-traceability.d.ts +28 -0
  78. package/dist/core/foundation/config-traceability.d.ts.map +1 -0
  79. package/dist/core/foundation/config-traceability.js +63 -0
  80. package/dist/core/foundation/config-traceability.js.map +1 -0
  81. package/dist/core/foundation/config-user.d.ts +32 -0
  82. package/dist/core/foundation/config-user.d.ts.map +1 -0
  83. package/dist/core/foundation/config-user.js +2 -0
  84. package/dist/core/foundation/config-user.js.map +1 -0
  85. package/dist/core/foundation/config-vcs.d.ts +29 -0
  86. package/dist/core/foundation/config-vcs.d.ts.map +1 -0
  87. package/dist/core/foundation/config-vcs.js +57 -0
  88. package/dist/core/foundation/config-vcs.js.map +1 -0
  89. package/dist/core/foundation/config.d.ts +23 -24
  90. package/dist/core/foundation/config.d.ts.map +1 -1
  91. package/dist/core/foundation/config.js +29 -36
  92. package/dist/core/foundation/config.js.map +1 -1
  93. package/dist/core/foundation/decisions-validate.d.ts +16 -0
  94. package/dist/core/foundation/decisions-validate.d.ts.map +1 -0
  95. package/dist/core/foundation/decisions-validate.js +74 -0
  96. package/dist/core/foundation/decisions-validate.js.map +1 -0
  97. package/dist/core/foundation/decisions.d.ts +34 -0
  98. package/dist/core/foundation/decisions.d.ts.map +1 -0
  99. package/dist/core/foundation/decisions.js +152 -0
  100. package/dist/core/foundation/decisions.js.map +1 -0
  101. package/dist/core/foundation/doctor-decisions.d.ts +9 -0
  102. package/dist/core/foundation/doctor-decisions.d.ts.map +1 -0
  103. package/dist/core/foundation/doctor-decisions.js +49 -0
  104. package/dist/core/foundation/doctor-decisions.js.map +1 -0
  105. package/dist/core/foundation/doctor-managed.d.ts.map +1 -1
  106. package/dist/core/foundation/doctor-managed.js +3 -0
  107. package/dist/core/foundation/doctor-managed.js.map +1 -1
  108. package/dist/core/foundation/doctor-prices.d.ts +8 -0
  109. package/dist/core/foundation/doctor-prices.d.ts.map +1 -0
  110. package/dist/core/foundation/doctor-prices.js +25 -0
  111. package/dist/core/foundation/doctor-prices.js.map +1 -0
  112. package/dist/core/foundation/doctor.d.ts.map +1 -1
  113. package/dist/core/foundation/doctor.js +20 -30
  114. package/dist/core/foundation/doctor.js.map +1 -1
  115. package/dist/core/foundation/harness-catalog.d.ts +7 -8
  116. package/dist/core/foundation/harness-catalog.d.ts.map +1 -1
  117. package/dist/core/foundation/harness-catalog.js +26 -41
  118. package/dist/core/foundation/harness-catalog.js.map +1 -1
  119. package/dist/core/foundation/init-blocks.d.ts +1 -1
  120. package/dist/core/foundation/init-blocks.d.ts.map +1 -1
  121. package/dist/core/foundation/init-blocks.js +51 -14
  122. package/dist/core/foundation/init-blocks.js.map +1 -1
  123. package/dist/core/foundation/init.d.ts +4 -0
  124. package/dist/core/foundation/init.d.ts.map +1 -1
  125. package/dist/core/foundation/init.js +7 -0
  126. package/dist/core/foundation/init.js.map +1 -1
  127. package/dist/core/foundation/new.d.ts +4 -1
  128. package/dist/core/foundation/new.d.ts.map +1 -1
  129. package/dist/core/foundation/new.js +18 -8
  130. package/dist/core/foundation/new.js.map +1 -1
  131. package/dist/core/foundation/package-root.d.ts +6 -0
  132. package/dist/core/foundation/package-root.d.ts.map +1 -1
  133. package/dist/core/foundation/package-root.js +6 -0
  134. package/dist/core/foundation/package-root.js.map +1 -1
  135. package/dist/core/foundation/rules-block.d.ts +11 -0
  136. package/dist/core/foundation/rules-block.d.ts.map +1 -0
  137. package/dist/core/foundation/rules-block.js +90 -0
  138. package/dist/core/foundation/rules-block.js.map +1 -0
  139. package/dist/core/foundation/traceability-block.d.ts +13 -0
  140. package/dist/core/foundation/traceability-block.d.ts.map +1 -0
  141. package/dist/core/foundation/traceability-block.js +124 -0
  142. package/dist/core/foundation/traceability-block.js.map +1 -0
  143. package/dist/core/lifecycle/done.d.ts.map +1 -1
  144. package/dist/core/lifecycle/done.js +2 -4
  145. package/dist/core/lifecycle/done.js.map +1 -1
  146. package/dist/core/lifecycle/recertify.d.ts +9 -0
  147. package/dist/core/lifecycle/recertify.d.ts.map +1 -0
  148. package/dist/core/lifecycle/recertify.js +27 -0
  149. package/dist/core/lifecycle/recertify.js.map +1 -0
  150. package/dist/core/lifecycle/reject.d.ts.map +1 -1
  151. package/dist/core/lifecycle/reject.js +3 -4
  152. package/dist/core/lifecycle/reject.js.map +1 -1
  153. package/dist/core/lifecycle/retry.d.ts.map +1 -1
  154. package/dist/core/lifecycle/retry.js +6 -20
  155. package/dist/core/lifecycle/retry.js.map +1 -1
  156. package/dist/core/lifecycle/verification-record.d.ts +42 -0
  157. package/dist/core/lifecycle/verification-record.d.ts.map +1 -0
  158. package/dist/core/lifecycle/verification-record.js +76 -0
  159. package/dist/core/lifecycle/verification-record.js.map +1 -0
  160. package/dist/core/report/approval-flags.d.ts +16 -3
  161. package/dist/core/report/approval-flags.d.ts.map +1 -1
  162. package/dist/core/report/approval-flags.js +59 -22
  163. package/dist/core/report/approval-flags.js.map +1 -1
  164. package/dist/core/report/brief-to-approval.d.ts +7 -0
  165. package/dist/core/report/brief-to-approval.d.ts.map +1 -0
  166. package/dist/core/report/brief-to-approval.js +43 -0
  167. package/dist/core/report/brief-to-approval.js.map +1 -0
  168. package/dist/core/report/planning-price-gaps.d.ts +10 -0
  169. package/dist/core/report/planning-price-gaps.d.ts.map +1 -0
  170. package/dist/core/report/planning-price-gaps.js +37 -0
  171. package/dist/core/report/planning-price-gaps.js.map +1 -0
  172. package/dist/core/report/planning-slice-lookup.d.ts.map +1 -1
  173. package/dist/core/report/planning-slice-lookup.js +3 -9
  174. package/dist/core/report/planning-slice-lookup.js.map +1 -1
  175. package/dist/core/report/recent-disclosures.d.ts +18 -0
  176. package/dist/core/report/recent-disclosures.d.ts.map +1 -0
  177. package/dist/core/report/recent-disclosures.js +105 -0
  178. package/dist/core/report/recent-disclosures.js.map +1 -0
  179. package/dist/core/report/record-disclosures.d.ts +14 -0
  180. package/dist/core/report/record-disclosures.d.ts.map +1 -0
  181. package/dist/core/report/record-disclosures.js +30 -0
  182. package/dist/core/report/record-disclosures.js.map +1 -0
  183. package/dist/core/report/record-estimates.d.ts +25 -0
  184. package/dist/core/report/record-estimates.d.ts.map +1 -0
  185. package/dist/core/report/record-estimates.js +83 -0
  186. package/dist/core/report/record-estimates.js.map +1 -0
  187. package/dist/core/report/record-rework.d.ts +19 -0
  188. package/dist/core/report/record-rework.d.ts.map +1 -0
  189. package/dist/core/report/record-rework.js +50 -0
  190. package/dist/core/report/record-rework.js.map +1 -0
  191. package/dist/core/report/record-verification.d.ts +16 -0
  192. package/dist/core/report/record-verification.d.ts.map +1 -0
  193. package/dist/core/report/record-verification.js +33 -0
  194. package/dist/core/report/record-verification.js.map +1 -0
  195. package/dist/core/report/report-dependencies.d.ts +29 -0
  196. package/dist/core/report/report-dependencies.d.ts.map +1 -0
  197. package/dist/core/report/report-dependencies.js +92 -0
  198. package/dist/core/report/report-dependencies.js.map +1 -0
  199. package/dist/core/report/report-events.d.ts +1 -0
  200. package/dist/core/report/report-events.d.ts.map +1 -1
  201. package/dist/core/report/report-events.js +8 -26
  202. package/dist/core/report/report-events.js.map +1 -1
  203. package/dist/core/report/report-mutation.d.ts +34 -0
  204. package/dist/core/report/report-mutation.d.ts.map +1 -0
  205. package/dist/core/report/report-mutation.js +191 -0
  206. package/dist/core/report/report-mutation.js.map +1 -0
  207. package/dist/core/report/report-scope.d.ts +21 -0
  208. package/dist/core/report/report-scope.d.ts.map +1 -0
  209. package/dist/core/report/report-scope.js +61 -0
  210. package/dist/core/report/report-scope.js.map +1 -0
  211. package/dist/core/report/report-traceability.d.ts +29 -0
  212. package/dist/core/report/report-traceability.d.ts.map +1 -0
  213. package/dist/core/report/report-traceability.js +94 -0
  214. package/dist/core/report/report-traceability.js.map +1 -0
  215. package/dist/core/report/report.d.ts +35 -1
  216. package/dist/core/report/report.d.ts.map +1 -1
  217. package/dist/core/report/report.js +130 -111
  218. package/dist/core/report/report.js.map +1 -1
  219. package/dist/core/report/result-sections.d.ts +30 -0
  220. package/dist/core/report/result-sections.d.ts.map +1 -0
  221. package/dist/core/report/result-sections.js +125 -0
  222. package/dist/core/report/result-sections.js.map +1 -0
  223. package/dist/core/report/scope-size.d.ts +7 -0
  224. package/dist/core/report/scope-size.d.ts.map +1 -0
  225. package/dist/core/report/scope-size.js +22 -0
  226. package/dist/core/report/scope-size.js.map +1 -0
  227. package/dist/core/run/commit-message.d.ts +24 -0
  228. package/dist/core/run/commit-message.d.ts.map +1 -0
  229. package/dist/core/run/commit-message.js +76 -0
  230. package/dist/core/run/commit-message.js.map +1 -0
  231. package/dist/core/run/dead-commit.d.ts +10 -0
  232. package/dist/core/run/dead-commit.d.ts.map +1 -0
  233. package/dist/core/run/dead-commit.js +70 -0
  234. package/dist/core/run/dead-commit.js.map +1 -0
  235. package/dist/core/run/focused-tests.d.ts +43 -0
  236. package/dist/core/run/focused-tests.d.ts.map +1 -0
  237. package/dist/core/run/focused-tests.js +116 -0
  238. package/dist/core/run/focused-tests.js.map +1 -0
  239. package/dist/core/run/manifest-approval.d.ts +7 -0
  240. package/dist/core/run/manifest-approval.d.ts.map +1 -0
  241. package/dist/core/run/manifest-approval.js +30 -0
  242. package/dist/core/run/manifest-approval.js.map +1 -0
  243. package/dist/core/run/manifest.d.ts +11 -1
  244. package/dist/core/run/manifest.d.ts.map +1 -1
  245. package/dist/core/run/manifest.js +52 -11
  246. package/dist/core/run/manifest.js.map +1 -1
  247. package/dist/core/run/mutation-report.d.ts +29 -0
  248. package/dist/core/run/mutation-report.d.ts.map +1 -0
  249. package/dist/core/run/mutation-report.js +107 -0
  250. package/dist/core/run/mutation-report.js.map +1 -0
  251. package/dist/core/run/mutation-run.d.ts +32 -0
  252. package/dist/core/run/mutation-run.d.ts.map +1 -0
  253. package/dist/core/run/mutation-run.js +80 -0
  254. package/dist/core/run/mutation-run.js.map +1 -0
  255. package/dist/core/run/verification.d.ts +12 -1
  256. package/dist/core/run/verification.d.ts.map +1 -1
  257. package/dist/core/run/verification.js +24 -2
  258. package/dist/core/run/verification.js.map +1 -1
  259. package/dist/core/spec/approve-worktree-shared.d.ts +33 -0
  260. package/dist/core/spec/approve-worktree-shared.d.ts.map +1 -0
  261. package/dist/core/spec/approve-worktree-shared.js +59 -0
  262. package/dist/core/spec/approve-worktree-shared.js.map +1 -0
  263. package/dist/core/spec/approve-worktree.d.ts +21 -0
  264. package/dist/core/spec/approve-worktree.d.ts.map +1 -0
  265. package/dist/core/spec/approve-worktree.js +153 -0
  266. package/dist/core/spec/approve-worktree.js.map +1 -0
  267. package/dist/core/spec/approve.d.ts +20 -10
  268. package/dist/core/spec/approve.d.ts.map +1 -1
  269. package/dist/core/spec/approve.js +57 -54
  270. package/dist/core/spec/approve.js.map +1 -1
  271. package/dist/core/spec/capability-impact.d.ts +43 -0
  272. package/dist/core/spec/capability-impact.d.ts.map +1 -0
  273. package/dist/core/spec/capability-impact.js +85 -0
  274. package/dist/core/spec/capability-impact.js.map +1 -0
  275. package/dist/core/spec/decisions-lint.d.ts +18 -0
  276. package/dist/core/spec/decisions-lint.d.ts.map +1 -0
  277. package/dist/core/spec/decisions-lint.js +127 -0
  278. package/dist/core/spec/decisions-lint.js.map +1 -0
  279. package/dist/core/spec/delta.d.ts +6 -0
  280. package/dist/core/spec/delta.d.ts.map +1 -1
  281. package/dist/core/spec/delta.js +73 -5
  282. package/dist/core/spec/delta.js.map +1 -1
  283. package/dist/core/spec/digest-decisions.d.ts +27 -0
  284. package/dist/core/spec/digest-decisions.d.ts.map +1 -0
  285. package/dist/core/spec/digest-decisions.js +86 -0
  286. package/dist/core/spec/digest-decisions.js.map +1 -0
  287. package/dist/core/spec/digest-format.d.ts +4 -0
  288. package/dist/core/spec/digest-format.d.ts.map +1 -0
  289. package/dist/core/spec/digest-format.js +44 -0
  290. package/dist/core/spec/digest-format.js.map +1 -0
  291. package/dist/core/spec/digest.d.ts +5 -3
  292. package/dist/core/spec/digest.d.ts.map +1 -1
  293. package/dist/core/spec/digest.js +14 -33
  294. package/dist/core/spec/digest.js.map +1 -1
  295. package/dist/core/spec/human-steps.d.ts +20 -0
  296. package/dist/core/spec/human-steps.d.ts.map +1 -0
  297. package/dist/core/spec/human-steps.js +52 -0
  298. package/dist/core/spec/human-steps.js.map +1 -0
  299. package/dist/core/spec/impact-lint.d.ts +24 -0
  300. package/dist/core/spec/impact-lint.d.ts.map +1 -0
  301. package/dist/core/spec/impact-lint.js +39 -0
  302. package/dist/core/spec/impact-lint.js.map +1 -0
  303. package/dist/core/spec/import-graph.d.ts +35 -0
  304. package/dist/core/spec/import-graph.d.ts.map +1 -0
  305. package/dist/core/spec/import-graph.js +155 -0
  306. package/dist/core/spec/import-graph.js.map +1 -0
  307. package/dist/core/spec/lint-findings.d.ts +44 -0
  308. package/dist/core/spec/lint-findings.d.ts.map +1 -0
  309. package/dist/core/spec/lint-findings.js +56 -0
  310. package/dist/core/spec/lint-findings.js.map +1 -0
  311. package/dist/core/spec/lint-output.d.ts +53 -0
  312. package/dist/core/spec/lint-output.d.ts.map +1 -0
  313. package/dist/core/spec/lint-output.js +87 -0
  314. package/dist/core/spec/lint-output.js.map +1 -0
  315. package/dist/core/spec/linter.d.ts +14 -4
  316. package/dist/core/spec/linter.d.ts.map +1 -1
  317. package/dist/core/spec/linter.js +271 -111
  318. package/dist/core/spec/linter.js.map +1 -1
  319. package/dist/core/spec/merged-spec-check.d.ts +24 -0
  320. package/dist/core/spec/merged-spec-check.d.ts.map +1 -0
  321. package/dist/core/spec/merged-spec-check.js +95 -0
  322. package/dist/core/spec/merged-spec-check.js.map +1 -0
  323. package/dist/core/spec/openspec-issues.d.ts +58 -0
  324. package/dist/core/spec/openspec-issues.d.ts.map +1 -0
  325. package/dist/core/spec/openspec-issues.js +166 -0
  326. package/dist/core/spec/openspec-issues.js.map +1 -0
  327. package/dist/core/spec/parser.d.ts +1 -0
  328. package/dist/core/spec/parser.d.ts.map +1 -1
  329. package/dist/core/spec/parser.js +16 -6
  330. package/dist/core/spec/parser.js.map +1 -1
  331. package/dist/core/spec/scenario-impact.d.ts +47 -0
  332. package/dist/core/spec/scenario-impact.d.ts.map +1 -0
  333. package/dist/core/spec/scenario-impact.js +130 -0
  334. package/dist/core/spec/scenario-impact.js.map +1 -0
  335. package/dist/core/spec/test-impact.d.ts +28 -0
  336. package/dist/core/spec/test-impact.d.ts.map +1 -0
  337. package/dist/core/spec/test-impact.js +119 -0
  338. package/dist/core/spec/test-impact.js.map +1 -0
  339. package/dist/core/spec/traceability-links.d.ts +50 -0
  340. package/dist/core/spec/traceability-links.d.ts.map +1 -0
  341. package/dist/core/spec/traceability-links.js +120 -0
  342. package/dist/core/spec/traceability-links.js.map +1 -0
  343. package/dist/core/spec/traceability-lint.d.ts +29 -0
  344. package/dist/core/spec/traceability-lint.d.ts.map +1 -0
  345. package/dist/core/spec/traceability-lint.js +143 -0
  346. package/dist/core/spec/traceability-lint.js.map +1 -0
  347. package/dist/core/status/blocked-item.d.ts +8 -0
  348. package/dist/core/status/blocked-item.d.ts.map +1 -0
  349. package/dist/core/status/blocked-item.js +45 -0
  350. package/dist/core/status/blocked-item.js.map +1 -0
  351. package/dist/core/status/change-locations.d.ts +44 -0
  352. package/dist/core/status/change-locations.d.ts.map +1 -0
  353. package/dist/core/status/change-locations.js +138 -0
  354. package/dist/core/status/change-locations.js.map +1 -0
  355. package/dist/core/status/dependency-readiness.d.ts +11 -0
  356. package/dist/core/status/dependency-readiness.d.ts.map +1 -0
  357. package/dist/core/status/dependency-readiness.js +39 -0
  358. package/dist/core/status/dependency-readiness.js.map +1 -0
  359. package/dist/core/status/inbox-projection.d.ts.map +1 -1
  360. package/dist/core/status/inbox-projection.js +23 -4
  361. package/dist/core/status/inbox-projection.js.map +1 -1
  362. package/dist/core/status/inbox-text.d.ts.map +1 -1
  363. package/dist/core/status/inbox-text.js +34 -3
  364. package/dist/core/status/inbox-text.js.map +1 -1
  365. package/dist/core/status/inbox.d.ts +10 -1
  366. package/dist/core/status/inbox.d.ts.map +1 -1
  367. package/dist/core/status/inbox.js +19 -1
  368. package/dist/core/status/inbox.js.map +1 -1
  369. package/dist/core/status/layout.d.ts +6 -0
  370. package/dist/core/status/layout.d.ts.map +1 -1
  371. package/dist/core/status/layout.js +8 -0
  372. package/dist/core/status/layout.js.map +1 -1
  373. package/dist/core/status/next-step.d.ts +17 -0
  374. package/dist/core/status/next-step.d.ts.map +1 -0
  375. package/dist/core/status/next-step.js +85 -0
  376. package/dist/core/status/next-step.js.map +1 -0
  377. package/dist/core/status/pre-spawn-words.d.ts +13 -0
  378. package/dist/core/status/pre-spawn-words.d.ts.map +1 -0
  379. package/dist/core/status/pre-spawn-words.js +26 -0
  380. package/dist/core/status/pre-spawn-words.js.map +1 -0
  381. package/dist/core/status/queue-parser.d.ts +1 -0
  382. package/dist/core/status/queue-parser.d.ts.map +1 -1
  383. package/dist/core/status/queue-parser.js +41 -2
  384. package/dist/core/status/queue-parser.js.map +1 -1
  385. package/dist/core/status/queue-planning.d.ts +1 -0
  386. package/dist/core/status/queue-planning.d.ts.map +1 -1
  387. package/dist/core/status/queue-planning.js +24 -12
  388. package/dist/core/status/queue-planning.js.map +1 -1
  389. package/dist/core/status/queue-report-detail.d.ts.map +1 -1
  390. package/dist/core/status/queue-report-detail.js +5 -3
  391. package/dist/core/status/queue-report-detail.js.map +1 -1
  392. package/dist/core/status/queue-state.d.ts +1 -1
  393. package/dist/core/status/queue-state.d.ts.map +1 -1
  394. package/dist/core/status/queue-state.js +27 -41
  395. package/dist/core/status/queue-state.js.map +1 -1
  396. package/dist/core/status/show.d.ts +25 -0
  397. package/dist/core/status/show.d.ts.map +1 -1
  398. package/dist/core/status/show.js +354 -91
  399. package/dist/core/status/show.js.map +1 -1
  400. package/dist/core/status/state.d.ts.map +1 -1
  401. package/dist/core/status/state.js +1 -23
  402. package/dist/core/status/state.js.map +1 -1
  403. package/dist/core/status/status.d.ts +18 -0
  404. package/dist/core/status/status.d.ts.map +1 -1
  405. package/dist/core/status/status.js +59 -80
  406. package/dist/core/status/status.js.map +1 -1
  407. package/dist/core/status/verification.d.ts +27 -0
  408. package/dist/core/status/verification.d.ts.map +1 -0
  409. package/dist/core/status/verification.js +111 -0
  410. package/dist/core/status/verification.js.map +1 -0
  411. package/dist/core/trace/function-ranges.d.ts +30 -0
  412. package/dist/core/trace/function-ranges.d.ts.map +1 -0
  413. package/dist/core/trace/function-ranges.js +250 -0
  414. package/dist/core/trace/function-ranges.js.map +1 -0
  415. package/dist/core/trace/mutation-pick.d.ts +55 -0
  416. package/dist/core/trace/mutation-pick.d.ts.map +1 -0
  417. package/dist/core/trace/mutation-pick.js +96 -0
  418. package/dist/core/trace/mutation-pick.js.map +1 -0
  419. package/dist/core/trace/scenario-index.d.ts +27 -0
  420. package/dist/core/trace/scenario-index.d.ts.map +1 -0
  421. package/dist/core/trace/scenario-index.js +76 -0
  422. package/dist/core/trace/scenario-index.js.map +1 -0
  423. package/dist/core/trace/scenario-lookup.d.ts +14 -0
  424. package/dist/core/trace/scenario-lookup.d.ts.map +1 -0
  425. package/dist/core/trace/scenario-lookup.js +179 -0
  426. package/dist/core/trace/scenario-lookup.js.map +1 -0
  427. package/dist/core/trace/tag-scan.d.ts +44 -0
  428. package/dist/core/trace/tag-scan.d.ts.map +1 -0
  429. package/dist/core/trace/tag-scan.js +200 -0
  430. package/dist/core/trace/tag-scan.js.map +1 -0
  431. package/dist/core/vcs/doctor-git.d.ts +13 -0
  432. package/dist/core/vcs/doctor-git.d.ts.map +1 -0
  433. package/dist/core/vcs/doctor-git.js +114 -0
  434. package/dist/core/vcs/doctor-git.js.map +1 -0
  435. package/dist/core/vcs/git-vcs-write.d.ts +47 -0
  436. package/dist/core/vcs/git-vcs-write.d.ts.map +1 -0
  437. package/dist/core/vcs/git-vcs-write.js +210 -0
  438. package/dist/core/vcs/git-vcs-write.js.map +1 -0
  439. package/dist/core/vcs/git-vcs.d.ts +40 -0
  440. package/dist/core/vcs/git-vcs.d.ts.map +1 -0
  441. package/dist/core/vcs/git-vcs.js +170 -0
  442. package/dist/core/vcs/git-vcs.js.map +1 -0
  443. package/dist/core/vcs/no-vcs.d.ts +26 -0
  444. package/dist/core/vcs/no-vcs.d.ts.map +1 -0
  445. package/dist/core/vcs/no-vcs.js +62 -0
  446. package/dist/core/vcs/no-vcs.js.map +1 -0
  447. package/dist/core/vcs/select.d.ts +11 -0
  448. package/dist/core/vcs/select.d.ts.map +1 -0
  449. package/dist/core/vcs/select.js +33 -0
  450. package/dist/core/vcs/select.js.map +1 -0
  451. package/dist/core/vcs/snapshot.d.ts +34 -0
  452. package/dist/core/vcs/snapshot.d.ts.map +1 -0
  453. package/dist/core/vcs/snapshot.js +71 -0
  454. package/dist/core/vcs/snapshot.js.map +1 -0
  455. package/dist/core/vcs/vcs.d.ts +55 -0
  456. package/dist/core/vcs/vcs.d.ts.map +1 -0
  457. package/dist/core/vcs/vcs.js +2 -0
  458. package/dist/core/vcs/vcs.js.map +1 -0
  459. package/dist/core/vcs/worktree.d.ts +10 -0
  460. package/dist/core/vcs/worktree.d.ts.map +1 -0
  461. package/dist/core/vcs/worktree.js +25 -0
  462. package/dist/core/vcs/worktree.js.map +1 -0
  463. package/dist/core/web/web-data-folders.d.ts.map +1 -1
  464. package/dist/core/web/web-data-folders.js +18 -33
  465. package/dist/core/web/web-data-folders.js.map +1 -1
  466. package/dist/core/web/web-data-lifecycle.d.ts.map +1 -1
  467. package/dist/core/web/web-data-lifecycle.js +2 -1
  468. package/dist/core/web/web-data-lifecycle.js.map +1 -1
  469. package/dist/core/web/web-events.d.ts +8 -6
  470. package/dist/core/web/web-events.d.ts.map +1 -1
  471. package/dist/core/web/web-events.js +7 -31
  472. package/dist/core/web/web-events.js.map +1 -1
  473. package/dist/core/web/web-server.d.ts.map +1 -1
  474. package/dist/core/web/web-server.js +3 -0
  475. package/dist/core/web/web-server.js.map +1 -1
  476. package/dist/core/web/web-trees.d.ts +27 -0
  477. package/dist/core/web/web-trees.d.ts.map +1 -0
  478. package/dist/core/web/web-trees.js +82 -0
  479. package/dist/core/web/web-trees.js.map +1 -0
  480. package/dist/harness/claude/claude-exec-args.d.ts +13 -0
  481. package/dist/harness/claude/claude-exec-args.d.ts.map +1 -0
  482. package/dist/harness/claude/claude-exec-args.js +61 -0
  483. package/dist/harness/claude/claude-exec-args.js.map +1 -0
  484. package/dist/harness/claude/claude-exec.d.ts +12 -0
  485. package/dist/harness/claude/claude-exec.d.ts.map +1 -0
  486. package/dist/harness/claude/claude-exec.js +79 -0
  487. package/dist/harness/claude/claude-exec.js.map +1 -0
  488. package/dist/harness/claude/claude-stream.d.ts +31 -0
  489. package/dist/harness/claude/claude-stream.d.ts.map +1 -0
  490. package/dist/harness/claude/claude-stream.js +150 -0
  491. package/dist/harness/claude/claude-stream.js.map +1 -0
  492. package/dist/harness/claude/claude-tools.d.ts +5 -0
  493. package/dist/harness/claude/claude-tools.d.ts.map +1 -0
  494. package/dist/harness/claude/claude-tools.js +30 -0
  495. package/dist/harness/claude/claude-tools.js.map +1 -0
  496. package/dist/harness/index.d.ts +1 -0
  497. package/dist/harness/index.d.ts.map +1 -1
  498. package/dist/harness/index.js +3 -0
  499. package/dist/harness/index.js.map +1 -1
  500. package/dist/harness/opencode/opencode-session.d.ts +42 -0
  501. package/dist/harness/opencode/opencode-session.d.ts.map +1 -0
  502. package/dist/harness/opencode/opencode-session.js +123 -0
  503. package/dist/harness/opencode/opencode-session.js.map +1 -0
  504. package/dist/harness/opencode/opencode.d.ts +2 -1
  505. package/dist/harness/opencode/opencode.d.ts.map +1 -1
  506. package/dist/harness/opencode/opencode.js +52 -28
  507. package/dist/harness/opencode/opencode.js.map +1 -1
  508. package/dist/harness/prompt.js +1 -1
  509. package/dist/harness/prompt.js.map +1 -1
  510. package/dist/harness/types.d.ts +112 -1
  511. package/dist/harness/types.d.ts.map +1 -1
  512. package/dist/harness/types.js.map +1 -1
  513. package/dist/index.d.ts +3 -1
  514. package/dist/index.d.ts.map +1 -1
  515. package/dist/index.js +1 -0
  516. package/dist/index.js.map +1 -1
  517. package/dist/testing/index.d.ts +3 -0
  518. package/dist/testing/index.d.ts.map +1 -0
  519. package/dist/testing/index.js +2 -0
  520. package/dist/testing/index.js.map +1 -0
  521. package/dist/testing/scenario.d.ts +29 -0
  522. package/dist/testing/scenario.d.ts.map +1 -0
  523. package/dist/testing/scenario.js +157 -0
  524. package/dist/testing/scenario.js.map +1 -0
  525. package/dist/watcher/archiver.d.ts +2 -1
  526. package/dist/watcher/archiver.d.ts.map +1 -1
  527. package/dist/watcher/archiver.js +27 -2
  528. package/dist/watcher/archiver.js.map +1 -1
  529. package/dist/watcher/auto-recertify.d.ts +27 -0
  530. package/dist/watcher/auto-recertify.d.ts.map +1 -0
  531. package/dist/watcher/auto-recertify.js +113 -0
  532. package/dist/watcher/auto-recertify.js.map +1 -0
  533. package/dist/watcher/auto-retry.d.ts +1 -1
  534. package/dist/watcher/auto-retry.d.ts.map +1 -1
  535. package/dist/watcher/auto-retry.js +3 -11
  536. package/dist/watcher/auto-retry.js.map +1 -1
  537. package/dist/watcher/baseline-key.d.ts +17 -0
  538. package/dist/watcher/baseline-key.d.ts.map +1 -0
  539. package/dist/watcher/baseline-key.js +31 -0
  540. package/dist/watcher/baseline-key.js.map +1 -0
  541. package/dist/watcher/baseline.d.ts +32 -0
  542. package/dist/watcher/baseline.d.ts.map +1 -0
  543. package/dist/watcher/baseline.js +166 -0
  544. package/dist/watcher/baseline.js.map +1 -0
  545. package/dist/watcher/blocked.d.ts +12 -0
  546. package/dist/watcher/blocked.d.ts.map +1 -0
  547. package/dist/watcher/blocked.js +24 -0
  548. package/dist/watcher/blocked.js.map +1 -0
  549. package/dist/watcher/dependencies.d.ts +28 -0
  550. package/dist/watcher/dependencies.d.ts.map +1 -0
  551. package/dist/watcher/dependencies.js +152 -0
  552. package/dist/watcher/dependencies.js.map +1 -0
  553. package/dist/watcher/done-metadata.d.ts +4 -0
  554. package/dist/watcher/done-metadata.d.ts.map +1 -0
  555. package/dist/watcher/done-metadata.js +20 -0
  556. package/dist/watcher/done-metadata.js.map +1 -0
  557. package/dist/watcher/failure-reason.d.ts +1 -1
  558. package/dist/watcher/failure-reason.d.ts.map +1 -1
  559. package/dist/watcher/focused-verify.d.ts +17 -0
  560. package/dist/watcher/focused-verify.d.ts.map +1 -0
  561. package/dist/watcher/focused-verify.js +54 -0
  562. package/dist/watcher/focused-verify.js.map +1 -0
  563. package/dist/watcher/git-guard.d.ts +24 -0
  564. package/dist/watcher/git-guard.d.ts.map +1 -0
  565. package/dist/watcher/git-guard.js +95 -0
  566. package/dist/watcher/git-guard.js.map +1 -0
  567. package/dist/watcher/instructions-drift.d.ts +21 -0
  568. package/dist/watcher/instructions-drift.d.ts.map +1 -0
  569. package/dist/watcher/instructions-drift.js +115 -0
  570. package/dist/watcher/instructions-drift.js.map +1 -0
  571. package/dist/watcher/loop.d.ts.map +1 -1
  572. package/dist/watcher/loop.js +36 -50
  573. package/dist/watcher/loop.js.map +1 -1
  574. package/dist/watcher/measures.d.ts.map +1 -1
  575. package/dist/watcher/measures.js +22 -39
  576. package/dist/watcher/measures.js.map +1 -1
  577. package/dist/watcher/mutation-check.d.ts +42 -0
  578. package/dist/watcher/mutation-check.d.ts.map +1 -0
  579. package/dist/watcher/mutation-check.js +147 -0
  580. package/dist/watcher/mutation-check.js.map +1 -0
  581. package/dist/watcher/regression.d.ts +10 -5
  582. package/dist/watcher/regression.d.ts.map +1 -1
  583. package/dist/watcher/regression.js +29 -22
  584. package/dist/watcher/regression.js.map +1 -1
  585. package/dist/watcher/runner.d.ts.map +1 -1
  586. package/dist/watcher/runner.js +12 -14
  587. package/dist/watcher/runner.js.map +1 -1
  588. package/dist/watcher/spawn.d.ts.map +1 -1
  589. package/dist/watcher/spawn.js +5 -0
  590. package/dist/watcher/spawn.js.map +1 -1
  591. package/dist/watcher/task-verify.d.ts +7 -2
  592. package/dist/watcher/task-verify.d.ts.map +1 -1
  593. package/dist/watcher/task-verify.js +33 -8
  594. package/dist/watcher/task-verify.js.map +1 -1
  595. package/dist/watcher/verify.d.ts +1 -5
  596. package/dist/watcher/verify.d.ts.map +1 -1
  597. package/dist/watcher/verify.js +2 -6
  598. package/dist/watcher/verify.js.map +1 -1
  599. package/package.json +5 -1
  600. package/templates/PLANNER.md +35 -9
  601. package/templates/openspec/config.yaml +1 -1
  602. package/templates/openspec/schemas/osq/schema.yaml +4 -2
  603. package/templates/openspec/schemas/osq/templates/proposal.md +9 -2
  604. package/templates/proposal.md +9 -2
  605. package/ui/dist/assets/{index-CZYeQ8la.js → index-D39uYbbL.js} +1 -1
  606. package/ui/dist/index.html +1 -1
@@ -1,20 +1,24 @@
1
- import { execFile } from 'node:child_process';
2
1
  import fs from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
  import { resolveScope } from '../run/scope.js';
5
4
  import { getArchiveDir, getChangesDir, getRejectedDir } from '../status/layout.js';
6
5
  import { compareNumericPrefix } from '../status/state.js';
6
+ import { collectDecisionsFindings } from './decisions-lint.js';
7
7
  import { DeltaMergeError, mergeDelta, parseDelta } from './delta.js';
8
8
  import { isExcludedChangePath } from './hasher.js';
9
+ import { collectImpactFindings } from './impact-lint.js';
10
+ import { buildImportGraph } from './import-graph.js';
11
+ import { LintFindingSet, makeFinding, } from './lint-findings.js';
12
+ import { validateMergedSpecs } from './merged-spec-check.js';
13
+ import { OPENSPEC_ERROR_PREFIX, appendUnsupportedNote, execFileCapture, scanOpenSpecIssues, } from './openspec-issues.js';
9
14
  import { OPENSPEC_EXPECTED_VERSION, assessOpenSpecVersion, formatInRangeWarning, } from './openspec-version.js';
10
15
  import { hasDeclaredWrites, parseFrontmatter, parseSpecMd, parseTaskMd, resolveChangeDoc, } from './parser.js';
16
+ import { collectTraceabilityFindings } from './traceability-lint.js';
11
17
  import { listNamedPaths, missingNamedPaths, namesExistingRepositoryPath, tokenizeVerifyCommand, } from './verify-paths.js';
12
18
  import { analyzeVerifyStarts, namedPathCoveredByScopes, } from './verify-starts.js';
13
- export { OPENSPEC_EXPECTED_VERSION };
19
+ export { OPENSPEC_EXPECTED_VERSION, OPENSPEC_ERROR_PREFIX };
14
20
  /** Remediation guidance citing ADR 004 for any validator pin violation. */
15
21
  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
22
  /**
19
23
  * ASCII control characters prohibited in authored change-folder files. Newline
20
24
  * (`\n`, 0x0A) and tab (`\t`, 0x09) are permitted as structural Markdown
@@ -204,6 +208,13 @@ function missingPackageScriptError(label, script) {
204
208
  function unresolvedVerifyWarning(label, command) {
205
209
  return `${label} verify names neither an existing repository path nor a package script: "${command}"`;
206
210
  }
211
+ function chainedVerifyError(label, command) {
212
+ return `Task in ${label} verify chains commands ("${command}"); move the chain into a package script and name that script, for example pnpm run <script>`;
213
+ }
214
+ /** Resolve an absolute path to its repository-relative POSIX form. */
215
+ function repositoryPath(projectRoot, absolutePath) {
216
+ return path.relative(projectRoot, absolutePath).split(path.sep).join('/');
217
+ }
207
218
  /**
208
219
  * Recursively list a change folder's authored files relative to `baseDir`,
209
220
  * applying the shared root-relative exclusion predicate. `.run`, `.git`, and
@@ -235,21 +246,21 @@ async function collectArtifactFiles(dir, baseDir) {
235
246
  return files;
236
247
  }
237
248
  /** Report the first prohibited control character found in `content`. */
238
- function scanControlCharacters(relPath, content, errors) {
249
+ function scanControlCharacters(relPath, file, content, findings) {
239
250
  const match = PROHIBITED_CONTROL_REGEX.exec(content);
240
251
  if (!match) {
241
252
  return;
242
253
  }
243
254
  const code = match[0].charCodeAt(0).toString(16).toUpperCase().padStart(2, '0');
244
- errors.push(`File ${relPath} contains prohibited control character (0x${code})`);
255
+ findings.error({ file }, `File ${relPath} contains prohibited control character (0x${code})`);
245
256
  }
246
257
  /** Report any task line carrying two or more acceptance checkbox items. */
247
- function scanFusedAcceptance(taskFile, content, errors) {
258
+ function scanFusedAcceptance(taskFile, file, content, findings) {
248
259
  const lines = content.split('\n');
249
260
  lines.forEach((line, index) => {
250
261
  const count = line.match(ACCEPTANCE_CHECKBOX_REGEX)?.length ?? 0;
251
262
  if (count > 1) {
252
- errors.push(`Task in ${taskFile} contains fused acceptance lines on line ${index + 1}`);
263
+ findings.error({ file }, `Task in ${taskFile} contains fused acceptance lines on line ${index + 1}`);
253
264
  }
254
265
  });
255
266
  }
@@ -397,18 +408,6 @@ export function parseOpenSpecFindings(output) {
397
408
  visit(parsed);
398
409
  return { errors, warnings };
399
410
  }
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
411
  /**
413
412
  * Maps a validator version assessment to the single finding it produces: none
414
413
  * for the pin, one warning inside the declared peer range, and one error citing
@@ -425,6 +424,68 @@ function versionFinding(assessment) {
425
424
  }
426
425
  return {};
427
426
  }
427
+ /** The proposal file an items-less OpenSpec finding is charged to. */
428
+ function legacyProposalFile(context) {
429
+ return context.changeFolder
430
+ ? `${context.openspecRoot}/changes/${context.changeFolder}/proposal.md`
431
+ : 'proposal.md';
432
+ }
433
+ /** Charge an items-less OpenSpec output to the linted change. */
434
+ function addLegacyFindings(output, context, findings) {
435
+ const file = legacyProposalFile(context);
436
+ const legacy = parseOpenSpecFindings(output);
437
+ for (const message of legacy.errors) {
438
+ findings.error({ file }, `${OPENSPEC_ERROR_PREFIX} ${appendUnsupportedNote(message)}`);
439
+ }
440
+ for (const message of legacy.warnings) {
441
+ findings.warning({ file }, `${OPENSPEC_ERROR_PREFIX} ${appendUnsupportedNote(message)}`);
442
+ }
443
+ return legacy.errors.length + legacy.warnings.length;
444
+ }
445
+ /** Run one validator command and fold its issues into the finding collector. */
446
+ async function collectCommandFindings(bin, args, projectRoot, timeout, context, findings, repository) {
447
+ const outcome = await execFileCapture(bin, args, {
448
+ cwd: projectRoot,
449
+ env: { ...process.env, OPENSPEC_TELEMETRY: '0' },
450
+ timeout,
451
+ });
452
+ const scan = await scanOpenSpecIssues(outcome.stdout, context);
453
+ let count;
454
+ if (scan === null) {
455
+ count = addLegacyFindings(outcome.stdout, context, findings);
456
+ }
457
+ else {
458
+ for (const finding of scan.findings) {
459
+ findings.addOwn(finding);
460
+ }
461
+ for (const finding of scan.repository) {
462
+ repository.push(finding);
463
+ }
464
+ count = scan.findings.length + scan.repository.length;
465
+ }
466
+ return {
467
+ code: outcome.code,
468
+ count,
469
+ detail: (outcome.stderr || outcome.stdout).trim().split('\n')[0],
470
+ };
471
+ }
472
+ /** Assess the installed validator and record the one version finding it yields. */
473
+ async function collectVersionFindings(projectRoot, logger, file, findings) {
474
+ const version = await resolveOpenSpecVersion(projectRoot);
475
+ if (!version) {
476
+ findings.error({ file }, `OpenSpec validator version could not be resolved from node_modules/@fission-ai/openspec/package.json. ${OPENSPEC_PIN_REMEDIATION}`);
477
+ return null;
478
+ }
479
+ logger?.info(`openspec version ${version}`);
480
+ const finding = versionFinding(await assessOpenSpecVersion(version));
481
+ if (finding.error !== undefined) {
482
+ findings.error({ file }, finding.error);
483
+ }
484
+ if (finding.warning !== undefined) {
485
+ findings.warning({ file }, finding.warning);
486
+ }
487
+ return version;
488
+ }
428
489
  /**
429
490
  * Runs the local OpenSpec validator for changes and specs. Validation failures
430
491
  * are returned prefixed with `openspec:`. A missing binary or a version outside
@@ -433,59 +494,48 @@ function versionFinding(assessment) {
433
494
  * ADR 005. Linting fails closed rather than silently degrading.
434
495
  */
435
496
  export async function validateWithOpenSpec(projectRoot, config, options = {}) {
436
- const logger = options.logger;
497
+ const file = 'package.json';
498
+ const findings = new LintFindingSet();
499
+ const repository = [];
437
500
  const bin = await resolveOpenSpecBin(projectRoot, config);
438
501
  if (!bin) {
502
+ findings.error({ file }, `OpenSpec validator binary is unavailable at node_modules/.bin/openspec. ${OPENSPEC_PIN_REMEDIATION}`);
439
503
  return {
440
- errors: [
441
- `OpenSpec validator binary is unavailable at node_modules/.bin/openspec. ${OPENSPEC_PIN_REMEDIATION}`,
442
- ],
443
- warnings: [],
504
+ errors: findings.errors(),
505
+ warnings: findings.warnings(),
506
+ findings: [...findings.findings],
507
+ repository,
444
508
  ran: false,
445
509
  bin: null,
446
510
  version: null,
447
511
  };
448
512
  }
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
- }
513
+ const version = await collectVersionFindings(projectRoot, options.logger, file, findings);
465
514
  const timeout = config.timeouts.verifyTimeoutSeconds * 1000;
515
+ const context = {
516
+ projectRoot,
517
+ openspecRoot: resolveOpenSpecRoot(config),
518
+ changeFolder: options.changeFolder ?? null,
519
+ };
466
520
  const commands = [
467
521
  ['validate', '--changes', '--strict', '--json', '--no-interactive'],
468
522
  ['validate', '--specs', '--strict', '--json', '--no-interactive'],
469
523
  ];
470
524
  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}` : ''}`);
525
+ const { code, count, detail } = await collectCommandFindings(bin, args, projectRoot, timeout, context, findings, repository);
526
+ if (code !== 0 && count === 0) {
527
+ findings.error({ file }, `${OPENSPEC_ERROR_PREFIX} ${args.join(' ')} exited with code ${code}${detail ? `: ${detail}` : ''}`);
486
528
  }
487
529
  }
488
- return { errors, warnings, ran: true, bin, version };
530
+ return {
531
+ errors: findings.errors(),
532
+ warnings: findings.warnings(),
533
+ findings: [...findings.findings],
534
+ repository,
535
+ ran: true,
536
+ bin,
537
+ version,
538
+ };
489
539
  }
490
540
  async function checkDependencyExists(projectRoot, depId, config) {
491
541
  const dirsToCheck = [
@@ -522,13 +572,13 @@ function instructionVerbOf(name) {
522
572
  * verb such as "update" or "document" are rejected with the capability and the
523
573
  * offending requirement title.
524
574
  */
525
- function scanInstructionShapedRequirements(capability, requirements, errors) {
575
+ function scanInstructionShapedRequirements(capability, deltaFile, requirements, findings) {
526
576
  for (const requirement of requirements) {
527
577
  const verb = instructionVerbOf(requirement.name);
528
578
  if (!verb) {
529
579
  continue;
530
580
  }
531
- errors.push(`${OPENSPEC_ERROR_PREFIX} ${capability}: requirement "${requirement.name}" is instruction-shaped ("${verb}"); delta specs must define living capability requirements, not instructions`);
581
+ findings.push(makeFinding('error', { file: deltaFile, requirement: requirement.name }, `${OPENSPEC_ERROR_PREFIX} ${capability}: requirement "${requirement.name}" is instruction-shaped ("${verb}"); delta specs must define living capability requirements, not instructions`));
532
582
  }
533
583
  }
534
584
  /**
@@ -536,29 +586,31 @@ function scanInstructionShapedRequirements(capability, requirements, errors) {
536
586
  * the living base spec at `openspec/specs/<capability>/spec.md`. Applying the
537
587
  * delta through the deterministic merge engine reports unmatched MODIFIED,
538
588
  * REMOVED, or RENAMED targets; ADDED-only deltas may create a new capability.
589
+ * Merged results are validated against their living spec's repository findings.
539
590
  */
540
- export async function verifyDeltaTargets(projectRoot, folderPath, config) {
541
- const errors = [];
591
+ export async function verifyDeltaTargets(projectRoot, folderPath, config, repositoryFindings = []) {
592
+ const findings = [];
593
+ const mergedSpecs = [];
542
594
  const deltasDir = path.join(folderPath, 'specs');
543
595
  let entries = [];
544
596
  try {
545
597
  entries = await fs.readdir(deltasDir, { withFileTypes: true });
546
598
  }
547
599
  catch {
548
- return errors;
600
+ return findings;
549
601
  }
550
602
  const capabilities = entries
551
603
  .filter((entry) => entry.isDirectory())
552
604
  .map((entry) => entry.name)
553
605
  .sort();
554
606
  if (capabilities.length === 0) {
555
- return errors;
607
+ return findings;
556
608
  }
557
609
  const specsRoot = path.join(projectRoot, resolveOpenSpecRoot(config), 'specs');
558
610
  for (const capability of capabilities) {
559
- const deltaContent = await fs
560
- .readFile(path.join(deltasDir, capability, 'spec.md'), 'utf8')
561
- .catch(() => null);
611
+ const deltaPath = path.join(deltasDir, capability, 'spec.md');
612
+ const deltaFile = repositoryPath(projectRoot, deltaPath);
613
+ const deltaContent = await fs.readFile(deltaPath, 'utf8').catch(() => null);
562
614
  if (deltaContent === null) {
563
615
  continue;
564
616
  }
@@ -566,20 +618,32 @@ export async function verifyDeltaTargets(projectRoot, folderPath, config) {
566
618
  .readFile(path.join(specsRoot, capability, 'spec.md'), 'utf8')
567
619
  .catch(() => null);
568
620
  const delta = parseDelta(deltaContent);
569
- scanInstructionShapedRequirements(capability, [...delta.added, ...delta.modified, ...delta.removed], errors);
621
+ scanInstructionShapedRequirements(capability, deltaFile, [...delta.added, ...delta.modified, ...delta.removed], findings);
570
622
  try {
571
- mergeDelta(baseContent, capability, delta);
623
+ const merged = mergeDelta(baseContent, capability, delta);
624
+ mergedSpecs.push({ capability, mergedContent: merged, deltaFile });
572
625
  }
573
626
  catch (error) {
574
627
  if (error instanceof DeltaMergeError) {
575
- errors.push(`${OPENSPEC_ERROR_PREFIX} ${capability}: ${error.message}`);
628
+ findings.push(makeFinding('error', { file: deltaFile }, `${OPENSPEC_ERROR_PREFIX} ${capability}: ${error.message}`));
576
629
  }
577
630
  else {
578
631
  throw error;
579
632
  }
580
633
  }
581
634
  }
582
- return errors;
635
+ if (mergedSpecs.length > 0) {
636
+ findings.push(...(await collectMergedSpecFindings(projectRoot, config, mergedSpecs, repositoryFindings)));
637
+ }
638
+ return findings;
639
+ }
640
+ /** Validate every merged spec when the validator binary is available. */
641
+ async function collectMergedSpecFindings(projectRoot, config, mergedSpecs, repositoryFindings) {
642
+ const bin = await resolveOpenSpecBin(projectRoot, config);
643
+ if (!bin) {
644
+ return [];
645
+ }
646
+ return validateMergedSpecs(config, bin, resolveOpenSpecRoot(config), mergedSpecs, repositoryFindings);
583
647
  }
584
648
  /**
585
649
  * Compares each pair of tasks' resolved existing files and returns one
@@ -600,7 +664,10 @@ function collectOverlapWarnings(scopes) {
600
664
  .filter((relativePath) => other.has(relativePath))
601
665
  .sort();
602
666
  for (const relativePath of shared) {
603
- warnings.push(`Tasks ${ordered[left].taskNumber} and ${ordered[right].taskNumber} share resolved scope file ${relativePath}`);
667
+ warnings.push({
668
+ file: relativePath,
669
+ message: `Tasks ${ordered[left].taskNumber} and ${ordered[right].taskNumber} share resolved scope file ${relativePath}`,
670
+ });
604
671
  }
605
672
  }
606
673
  }
@@ -618,7 +685,10 @@ function collectHarnessFixtureWarnings(scopes) {
618
685
  const hasHarness = scope.existingPaths.some((entry) => entry.startsWith('src/harness/'));
619
686
  const hasFixtures = scope.existingPaths.some((entry) => entry === 'tests/fixtures/events' || entry.startsWith('tests/fixtures/events/'));
620
687
  if (hasHarness && !hasFixtures) {
621
- warnings.push(`Task in ${scope.taskFile} resolves src/harness/ scope but no file under tests/fixtures/events/`);
688
+ warnings.push({
689
+ file: scope.taskPath,
690
+ message: `Task in ${scope.taskFile} resolves src/harness/ scope but no file under tests/fixtures/events/`,
691
+ });
622
692
  }
623
693
  }
624
694
  return warnings;
@@ -650,85 +720,107 @@ function readTestsModifyDeclaration(content) {
650
720
  return { present: false, valid: true };
651
721
  }
652
722
  export async function lintChangeFolder(projectRoot, folderPath, config, options = {}) {
653
- const errors = [];
654
- const warnings = [];
723
+ const findings = new LintFindingSet();
655
724
  const packageScripts = await readRootPackageScripts(projectRoot);
656
725
  // Check: no authored change-folder file contains prohibited control characters
657
726
  for (const relPath of await collectArtifactFiles(folderPath, folderPath)) {
658
727
  const content = await fs.readFile(path.join(folderPath, relPath), 'utf8');
659
- scanControlCharacters(relPath, content, errors);
728
+ scanControlCharacters(relPath, repositoryPath(projectRoot, path.join(folderPath, relPath)), content, findings);
660
729
  }
661
730
  const resolvedDoc = await resolveChangeDoc(folderPath);
662
731
  if (!resolvedDoc) {
663
- errors.push('proposal.md (or legacy spec.md) not found in change folder');
664
- return { valid: false, errors, warnings };
732
+ findings.error({ file: repositoryPath(projectRoot, path.join(folderPath, 'proposal.md')) }, 'proposal.md (or legacy spec.md) not found in change folder');
733
+ return lintResult(findings);
665
734
  }
735
+ const docRepoPath = repositoryPath(projectRoot, resolvedDoc.path);
666
736
  const specContent = await fs.readFile(resolvedDoc.path, 'utf8');
667
737
  const spec = parseSpecMd(specContent);
668
738
  // Check: proposals must declare a change-level verify command
669
739
  if (resolvedDoc.kind === 'proposal' && !spec.verify) {
670
- errors.push('proposal.md must declare a verify command in frontmatter');
740
+ findings.error({ file: docRepoPath }, 'proposal.md must declare a verify command in frontmatter');
671
741
  }
672
742
  // Check: proposals must declare a non-empty ## Surface section; legacy spec.md
673
743
  // change documents are exempt exactly as they are for the verify check.
674
744
  if (resolvedDoc.kind === 'proposal' &&
675
745
  proposalSurfaceIsEmpty(parseFrontmatter(specContent).body)) {
676
- errors.push(PROPOSAL_SURFACE_ERROR);
746
+ findings.error({ file: docRepoPath, section: 'Surface' }, PROPOSAL_SURFACE_ERROR);
747
+ }
748
+ // Check: Decisions section and project rules block, when the project has ADRs.
749
+ // A legacy spec.md change document is exempt exactly as it is for Surface.
750
+ if (resolvedDoc.kind === 'proposal') {
751
+ for (const finding of await collectDecisionsFindings({
752
+ projectRoot,
753
+ folderPath,
754
+ proposalPath: docRepoPath,
755
+ proposalBody: parseFrontmatter(specContent).body,
756
+ config,
757
+ })) {
758
+ findings.addOwn(finding);
759
+ }
677
760
  }
678
761
  // Check: the change-level verify participates in the same trust analysis
679
762
  if (spec.verify) {
680
763
  const docLabel = path.basename(resolvedDoc.path);
681
764
  const analysis = await analyzeVerifyCommand(projectRoot, spec.verify, packageScripts);
682
765
  if (analysis.placeholder) {
683
- errors.push(placeholderVerifyError(docLabel));
766
+ findings.error({ file: docRepoPath }, placeholderVerifyError(docLabel));
684
767
  }
685
768
  else if (!chainsVerifyCommand(spec.verify)) {
686
769
  if (analysis.missingScript !== null) {
687
- errors.push(missingPackageScriptError(docLabel, analysis.missingScript));
770
+ findings.error({ file: docRepoPath }, missingPackageScriptError(docLabel, analysis.missingScript));
688
771
  }
689
772
  else if (analysis.unresolved) {
690
- warnings.push(unresolvedVerifyWarning(docLabel, spec.verify));
773
+ findings.warning({ file: docRepoPath }, unresolvedVerifyWarning(docLabel, spec.verify));
691
774
  }
692
775
  }
693
776
  }
694
777
  // Check: features.writes is retired; delta specs are the sole writes declaration
695
778
  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');
779
+ findings.error({ file: docRepoPath }, 'features.writes is no longer supported in proposal frontmatter; write declarations are derived strictly from delta specs under specs/<capability>/spec.md');
697
780
  }
698
781
  // Check: contract tables count
699
782
  if (spec.contractTablesCount > config.limits.maxContractTables) {
700
- errors.push(`Contract has ${spec.contractTablesCount} tables (max allowed is ${config.limits.maxContractTables})`);
783
+ findings.error({ file: docRepoPath }, `Contract has ${spec.contractTablesCount} tables (max allowed is ${config.limits.maxContractTables})`);
701
784
  }
702
785
  // Check: depends_on exists
703
786
  for (const dep of spec.dependsOn) {
704
787
  const exists = await checkDependencyExists(projectRoot, dep, config);
705
788
  if (!exists) {
706
- errors.push(`depends_on names missing change: ${dep}`);
789
+ findings.error({ file: docRepoPath }, `depends_on names missing change: ${dep}`);
790
+ }
791
+ }
792
+ // Check: fixes names an existing active, archived, or rejected change
793
+ for (const fixed of spec.fixes) {
794
+ const exists = await checkDependencyExists(projectRoot, fixed, config);
795
+ if (!exists) {
796
+ findings.error({ file: docRepoPath }, `fixes names missing change: ${fixed}`);
707
797
  }
708
798
  }
709
799
  // Tasks checks
710
800
  const tasksDir = path.join(folderPath, 'tasks');
801
+ const tasksRepoPath = repositoryPath(projectRoot, tasksDir);
711
802
  let taskEntries = [];
712
803
  try {
713
804
  taskEntries = (await fs.readdir(tasksDir)).filter((e) => e.endsWith('.md'));
714
805
  }
715
806
  catch {
716
- errors.push('tasks directory not found in change folder');
807
+ findings.error({ file: tasksRepoPath }, 'tasks directory not found in change folder');
717
808
  }
718
809
  if (taskEntries.length === 0) {
719
- errors.push('No task files found under tasks/');
810
+ findings.error({ file: tasksRepoPath }, 'No task files found under tasks/');
720
811
  }
721
812
  const resolvedTaskScopes = [];
722
813
  for (const taskFile of taskEntries) {
723
814
  const taskPath = path.join(tasksDir, taskFile);
815
+ const taskRepoPath = repositoryPath(projectRoot, taskPath);
724
816
  const taskContent = await fs.readFile(taskPath, 'utf8');
725
817
  const task = parseTaskMd(taskContent);
726
818
  // Check: acceptance items must each occupy their own line
727
- scanFusedAcceptance(taskFile, taskContent, errors);
819
+ scanFusedAcceptance(taskFile, taskRepoPath, taskContent, findings);
728
820
  // Check: tests.modify, when declared, must be a boolean
729
821
  const testsModify = readTestsModifyDeclaration(taskContent);
730
822
  if (testsModify.present && !testsModify.valid) {
731
- errors.push(`Task in ${taskFile} tests.modify must be a boolean (got ${JSON.stringify(testsModify.value)})`);
823
+ findings.error({ file: taskRepoPath }, `Task in ${taskFile} tests.modify must be a boolean (got ${JSON.stringify(testsModify.value)})`);
732
824
  }
733
825
  // Resolve the declared scope once through the shared deterministic resolver.
734
826
  const resolved = task.scope.length > 0 ? await resolveScope(projectRoot, task.scope) : [];
@@ -742,7 +834,9 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
742
834
  resolvedTaskScopes.push({
743
835
  taskFile,
744
836
  taskNumber: taskFile.replace(/\.md$/, ''),
837
+ taskPath: taskRepoPath,
745
838
  existingPaths,
839
+ resolvedPaths: resolved.map((entry) => entry.relativePath),
746
840
  task,
747
841
  verifyAnalysis: analysis,
748
842
  });
@@ -750,35 +844,39 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
750
844
  if (!task.testsModify && existingPaths.length > 0) {
751
845
  const touched = existingPaths.filter(isTestFilePath).sort();
752
846
  if (touched.length > 0) {
753
- errors.push(`Task in ${taskFile} scope touches existing test files (${touched.join(', ')}) without tests.modify: true`);
847
+ findings.error({ file: taskRepoPath }, `Task in ${taskFile} scope touches existing test files (${touched.join(', ')}) without tests.modify: true`);
754
848
  }
755
849
  }
756
850
  // Check: verify command empty, placeholder, chained, or missing script
757
851
  if (analysis === null) {
758
- errors.push(`Task in ${taskFile} verify command is empty`);
852
+ findings.error({ file: taskRepoPath }, `Task in ${taskFile} verify command is empty`);
759
853
  }
760
854
  else if (analysis.placeholder) {
761
- errors.push(placeholderVerifyError(taskLabel));
855
+ findings.error({ file: taskRepoPath }, placeholderVerifyError(taskLabel));
762
856
  }
763
857
  else if (chainsVerifyCommand(task.verify)) {
764
- errors.push(`Task in ${taskFile} verify chains commands ("${task.verify}")`);
858
+ findings.error({ file: taskRepoPath }, chainedVerifyError(taskFile, task.verify));
765
859
  }
766
860
  else if (analysis.missingScript !== null) {
767
- errors.push(missingPackageScriptError(taskLabel, analysis.missingScript));
861
+ findings.error({ file: taskRepoPath }, missingPackageScriptError(taskLabel, analysis.missingScript));
768
862
  }
769
863
  // Check: acceptance checklist length
770
864
  if (task.acceptance.length > config.limits.maxAcceptanceLines) {
771
- errors.push(`Task in ${taskFile} acceptance lines (${task.acceptance.length}) exceeds limit ${config.limits.maxAcceptanceLines}`);
865
+ findings.error({ file: taskRepoPath }, `Task in ${taskFile} acceptance lines (${task.acceptance.length}) exceeds limit ${config.limits.maxAcceptanceLines}`);
772
866
  }
773
867
  // Check: scope expands to more than maxScopeFiles
774
868
  if (task.scope.length > config.limits.maxScopeFiles) {
775
- errors.push(`Task in ${taskFile} scope specifies ${task.scope.length} patterns, exceeding limit ${config.limits.maxScopeFiles}`);
869
+ findings.error({ file: taskRepoPath }, `Task in ${taskFile} scope specifies ${task.scope.length} patterns, exceeding limit ${config.limits.maxScopeFiles}`);
776
870
  }
777
871
  }
778
872
  // Check: harness scope should cover the event fixtures without failing lint
779
- warnings.push(...collectHarnessFixtureWarnings(resolvedTaskScopes));
873
+ for (const entry of collectHarnessFixtureWarnings(resolvedTaskScopes)) {
874
+ findings.warning({ file: entry.file }, entry.message);
875
+ }
780
876
  // Check: shared resolved files across tasks are reported as non-failing warnings
781
- warnings.push(...collectOverlapWarnings(resolvedTaskScopes));
877
+ for (const entry of collectOverlapWarnings(resolvedTaskScopes)) {
878
+ findings.warning({ file: entry.file }, entry.message);
879
+ }
782
880
  // Tasks are analyzed in numeric order so earlier scopes can cover a later
783
881
  // task's new paths. The unresolved-target warning is suppressed when every
784
882
  // missing named path is one the task's own or an earlier task's scope covers.
@@ -791,7 +889,7 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
791
889
  const covered = namedPaths.length > 0 &&
792
890
  missingPaths.every((namedPath) => namedPathCoveredByScopes(namedPath, [scope.task.scope, ...earlierScopes]));
793
891
  if (!covered) {
794
- warnings.push(unresolvedVerifyWarning(`Task in ${scope.taskFile}`, scope.task.verify));
892
+ findings.warning({ file: scope.taskPath }, unresolvedVerifyWarning(`Task in ${scope.taskFile}`, scope.task.verify));
795
893
  }
796
894
  }
797
895
  earlierScopes.push([...scope.task.scope]);
@@ -806,21 +904,83 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
806
904
  }));
807
905
  const verifyStarts = await analyzeVerifyStarts(projectRoot, verifyStartTasks);
808
906
  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`);
907
+ findings.warning({ file: taskRepoPathFor(projectRoot, tasksDir, contradiction.taskNumber) }, `Task in ${contradiction.taskNumber}.md declares verify_starts: ${contradiction.start} but its verify names ${contradiction.path}, which the task creates`);
810
908
  }
811
909
  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);
910
+ findings.warning({ file: taskRepoPathFor(projectRoot, tasksDir, uncreatable.taskNumber) }, `Task in ${uncreatable.taskNumber}.md verify names ${uncreatable.path}, which no task in the change can create`);
911
+ }
912
+ // Check: import-graph impact warnings never fail the change. The graph is
913
+ // built once per lint run by the CLI and passed through `LintOptions`.
914
+ const importGraph = options.importGraph ??
915
+ (await buildImportGraph(projectRoot, { skip: [config.paths.openspecRoot] }));
916
+ for (const finding of await collectImpactFindings({
917
+ projectRoot,
918
+ folderPath,
919
+ config,
920
+ proposalPath: docRepoPath,
921
+ reads: spec.features.reads,
922
+ tasks: resolvedTaskScopes.map((scope) => ({
923
+ taskNumber: scope.taskNumber,
924
+ taskPath: scope.taskPath,
925
+ existingPaths: scope.existingPaths,
926
+ testsModify: scope.task.testsModify,
927
+ verify: scope.task.verify,
928
+ })),
929
+ importGraph,
930
+ })) {
931
+ findings.addOwn(finding);
932
+ }
933
+ // Check: scenario traceability links and the blast radius of changed
934
+ // scenarios. Reuses the graph already built for impact lint and builds the
935
+ // scenario index once.
936
+ for (const finding of await collectTraceabilityFindings({
937
+ projectRoot,
938
+ folderPath,
939
+ config,
940
+ proposalPath: docRepoPath,
941
+ tasks: resolvedTaskScopes.map((scope) => ({
942
+ taskNumber: scope.taskNumber,
943
+ taskPath: scope.taskPath,
944
+ resolvedPaths: scope.resolvedPaths,
945
+ testsModify: scope.task.testsModify,
946
+ })),
947
+ importGraph,
948
+ })) {
949
+ findings.addOwn(finding);
950
+ }
951
+ // Check: pinned OpenSpec validator and strict validation. Runs before the
952
+ // delta check so living-spec repository findings can suppress inherited
953
+ // warnings in the merged specs.
954
+ const changeFolder = path.basename(folderPath);
955
+ const openSpec = await validateWithOpenSpec(projectRoot, config, {
956
+ ...options,
957
+ changeFolder,
958
+ });
959
+ for (const finding of openSpec.findings) {
960
+ findings.addOwn(finding);
961
+ }
962
+ for (const finding of openSpec.repository) {
963
+ findings.addRepository(finding);
964
+ }
965
+ // Check: delta targets resolve against living base specs before approval and
966
+ // the living spec each delta will produce validates after archive.
967
+ for (const finding of await verifyDeltaTargets(projectRoot, folderPath, config, openSpec.repository)) {
968
+ findings.addOwn(finding);
969
+ }
970
+ return lintResult(findings);
971
+ }
972
+ /** Repository path of a task file named by its numeric task number. */
973
+ function taskRepoPathFor(projectRoot, tasksDir, taskNumber) {
974
+ return repositoryPath(projectRoot, path.join(tasksDir, `${taskNumber}.md`));
975
+ }
976
+ /** Assemble the public lint result from the accumulated findings. */
977
+ function lintResult(findings) {
820
978
  return {
821
- valid: errors.length === 0,
822
- errors,
823
- warnings,
979
+ valid: findings.errors().length === 0,
980
+ errors: findings.errors(),
981
+ warnings: findings.warnings(),
982
+ findings: [...findings.findings],
983
+ repository: [...findings.repository],
824
984
  };
825
985
  }
826
986
  //# sourceMappingURL=linter.js.map