wingfoil 0.2.2 → 0.3.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 (454) hide show
  1. package/README.md +83 -43
  2. package/dist/agent/discovery.d.ts +72 -0
  3. package/dist/agent/discovery.d.ts.map +1 -0
  4. package/dist/agent/discovery.js +210 -0
  5. package/dist/agent/discovery.js.map +1 -0
  6. package/dist/agent/execute.d.ts +206 -0
  7. package/dist/agent/execute.d.ts.map +1 -0
  8. package/dist/agent/execute.js +581 -0
  9. package/dist/agent/execute.js.map +1 -0
  10. package/dist/agent/index.d.ts +23 -0
  11. package/dist/agent/index.d.ts.map +1 -0
  12. package/dist/agent/index.js +85 -0
  13. package/dist/agent/index.js.map +1 -0
  14. package/dist/agent/launch.d.ts +133 -0
  15. package/dist/agent/launch.d.ts.map +1 -0
  16. package/dist/agent/launch.js +380 -0
  17. package/dist/agent/launch.js.map +1 -0
  18. package/dist/agent/manifest.d.ts +30 -0
  19. package/dist/agent/manifest.d.ts.map +1 -0
  20. package/dist/agent/manifest.js +93 -0
  21. package/dist/agent/manifest.js.map +1 -0
  22. package/dist/agent/placeholders.d.ts +49 -0
  23. package/dist/agent/placeholders.d.ts.map +1 -0
  24. package/dist/agent/placeholders.js +177 -0
  25. package/dist/agent/placeholders.js.map +1 -0
  26. package/dist/agent/run-log.d.ts +254 -0
  27. package/dist/agent/run-log.d.ts.map +1 -0
  28. package/dist/agent/run-log.js +612 -0
  29. package/dist/agent/run-log.js.map +1 -0
  30. package/dist/agent/schema.d.ts +100 -0
  31. package/dist/agent/schema.d.ts.map +1 -0
  32. package/dist/agent/schema.js +89 -0
  33. package/dist/agent/schema.js.map +1 -0
  34. package/dist/agent/step.d.ts +41 -0
  35. package/dist/agent/step.d.ts.map +1 -0
  36. package/dist/agent/step.js +95 -0
  37. package/dist/agent/step.js.map +1 -0
  38. package/dist/build-info.json +4 -0
  39. package/dist/cli/error.d.ts +11 -2
  40. package/dist/cli/error.d.ts.map +1 -1
  41. package/dist/cli/error.js +27 -6
  42. package/dist/cli/error.js.map +1 -1
  43. package/dist/cli/exit.d.ts +33 -3
  44. package/dist/cli/exit.d.ts.map +1 -1
  45. package/dist/cli/exit.js +54 -3
  46. package/dist/cli/exit.js.map +1 -1
  47. package/dist/cli/index.d.ts +1 -1
  48. package/dist/cli/index.d.ts.map +1 -1
  49. package/dist/cli/index.js +2 -1
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/cli/init-command.d.ts.map +1 -1
  52. package/dist/cli/init-command.js +4 -2
  53. package/dist/cli/init-command.js.map +1 -1
  54. package/dist/cli/mcp-command.d.ts +13 -4
  55. package/dist/cli/mcp-command.d.ts.map +1 -1
  56. package/dist/cli/mcp-command.js +25 -8
  57. package/dist/cli/mcp-command.js.map +1 -1
  58. package/dist/cli/output.d.ts +12 -4
  59. package/dist/cli/output.d.ts.map +1 -1
  60. package/dist/cli/output.js +15 -4
  61. package/dist/cli/output.js.map +1 -1
  62. package/dist/cli/program.d.ts +1 -1
  63. package/dist/cli/program.d.ts.map +1 -1
  64. package/dist/cli/program.js +162 -40
  65. package/dist/cli/program.js.map +1 -1
  66. package/dist/cli/registrar.d.ts +1 -1
  67. package/dist/cli/registrar.d.ts.map +1 -1
  68. package/dist/cli/registrar.js +67 -6
  69. package/dist/cli/registrar.js.map +1 -1
  70. package/dist/cli/suggest.d.ts +20 -0
  71. package/dist/cli/suggest.d.ts.map +1 -0
  72. package/dist/cli/suggest.js +49 -0
  73. package/dist/cli/suggest.js.map +1 -0
  74. package/dist/cli/warning.d.ts +26 -0
  75. package/dist/cli/warning.d.ts.map +1 -0
  76. package/dist/cli/warning.js +66 -0
  77. package/dist/cli/warning.js.map +1 -0
  78. package/dist/cli.js +11 -2
  79. package/dist/cli.js.map +1 -1
  80. package/dist/core/agent-execute.d.ts +44 -0
  81. package/dist/core/agent-execute.d.ts.map +1 -0
  82. package/dist/core/agent-execute.js +71 -0
  83. package/dist/core/agent-execute.js.map +1 -0
  84. package/dist/core/agent-list.d.ts +95 -0
  85. package/dist/core/agent-list.d.ts.map +1 -0
  86. package/dist/core/agent-list.js +207 -0
  87. package/dist/core/agent-list.js.map +1 -0
  88. package/dist/core/agent-show.d.ts +44 -0
  89. package/dist/core/agent-show.d.ts.map +1 -0
  90. package/dist/core/agent-show.js +188 -0
  91. package/dist/core/agent-show.js.map +1 -0
  92. package/dist/core/approval-authority.d.ts +17 -14
  93. package/dist/core/approval-authority.d.ts.map +1 -1
  94. package/dist/core/approval-authority.js +9 -9
  95. package/dist/core/approval-authority.js.map +1 -1
  96. package/dist/core/builtin-asset.d.ts +18 -15
  97. package/dist/core/builtin-asset.d.ts.map +1 -1
  98. package/dist/core/builtin-asset.js +17 -14
  99. package/dist/core/builtin-asset.js.map +1 -1
  100. package/dist/core/builtin-integrity.d.ts +12 -2
  101. package/dist/core/builtin-integrity.d.ts.map +1 -1
  102. package/dist/core/builtin-integrity.js +85 -9
  103. package/dist/core/builtin-integrity.js.map +1 -1
  104. package/dist/core/confinement.d.ts +3 -1
  105. package/dist/core/confinement.d.ts.map +1 -1
  106. package/dist/core/confinement.js +7 -4
  107. package/dist/core/confinement.js.map +1 -1
  108. package/dist/core/context.d.ts +150 -98
  109. package/dist/core/context.d.ts.map +1 -1
  110. package/dist/core/context.js +602 -66
  111. package/dist/core/context.js.map +1 -1
  112. package/dist/core/directive-assign.d.ts +33 -7
  113. package/dist/core/directive-assign.d.ts.map +1 -1
  114. package/dist/core/directive-assign.js +67 -27
  115. package/dist/core/directive-assign.js.map +1 -1
  116. package/dist/core/directive-scope.d.ts +36 -0
  117. package/dist/core/directive-scope.d.ts.map +1 -0
  118. package/dist/core/directive-scope.js +34 -0
  119. package/dist/core/directive-scope.js.map +1 -0
  120. package/dist/core/directives-list.d.ts +13 -3
  121. package/dist/core/directives-list.d.ts.map +1 -1
  122. package/dist/core/directives-list.js +18 -6
  123. package/dist/core/directives-list.js.map +1 -1
  124. package/dist/core/dry-run.d.ts +33 -0
  125. package/dist/core/dry-run.d.ts.map +1 -0
  126. package/dist/core/dry-run.js +45 -0
  127. package/dist/core/dry-run.js.map +1 -0
  128. package/dist/core/element-ref.d.ts +23 -0
  129. package/dist/core/element-ref.d.ts.map +1 -0
  130. package/dist/core/element-ref.js +24 -0
  131. package/dist/core/element-ref.js.map +1 -0
  132. package/dist/core/error-details.d.ts +20 -0
  133. package/dist/core/error-details.d.ts.map +1 -0
  134. package/dist/core/error-details.js +65 -0
  135. package/dist/core/error-details.js.map +1 -0
  136. package/dist/core/exit-code.d.ts.map +1 -1
  137. package/dist/core/exit-code.js +6 -7
  138. package/dist/core/exit-code.js.map +1 -1
  139. package/dist/core/git-identity.d.ts +46 -11
  140. package/dist/core/git-identity.d.ts.map +1 -1
  141. package/dist/core/git-identity.js +140 -27
  142. package/dist/core/git-identity.js.map +1 -1
  143. package/dist/core/index.d.ts +184 -10
  144. package/dist/core/index.d.ts.map +1 -1
  145. package/dist/core/index.js +1045 -185
  146. package/dist/core/index.js.map +1 -1
  147. package/dist/core/init.d.ts +45 -5
  148. package/dist/core/init.d.ts.map +1 -1
  149. package/dist/core/init.js +134 -24
  150. package/dist/core/init.js.map +1 -1
  151. package/dist/core/loaders.d.ts +170 -14
  152. package/dist/core/loaders.d.ts.map +1 -1
  153. package/dist/core/loaders.js +570 -89
  154. package/dist/core/loaders.js.map +1 -1
  155. package/dist/core/memory-add-link.d.ts +28 -0
  156. package/dist/core/memory-add-link.d.ts.map +1 -0
  157. package/dist/core/memory-add-link.js +67 -0
  158. package/dist/core/memory-add-link.js.map +1 -0
  159. package/dist/core/memory-add-type.d.ts +25 -1
  160. package/dist/core/memory-add-type.d.ts.map +1 -1
  161. package/dist/core/memory-add-type.js +59 -1
  162. package/dist/core/memory-add-type.js.map +1 -1
  163. package/dist/core/memory-amend.d.ts +80 -0
  164. package/dist/core/memory-amend.d.ts.map +1 -0
  165. package/dist/core/memory-amend.js +160 -0
  166. package/dist/core/memory-amend.js.map +1 -0
  167. package/dist/core/memory-supersede.d.ts +28 -0
  168. package/dist/core/memory-supersede.d.ts.map +1 -0
  169. package/dist/core/memory-supersede.js +79 -0
  170. package/dist/core/memory-supersede.js.map +1 -0
  171. package/dist/core/memory-transition.d.ts +190 -30
  172. package/dist/core/memory-transition.d.ts.map +1 -1
  173. package/dist/core/memory-transition.js +480 -59
  174. package/dist/core/memory-transition.js.map +1 -1
  175. package/dist/core/registry.d.ts +82 -10
  176. package/dist/core/registry.d.ts.map +1 -1
  177. package/dist/core/registry.js +50 -0
  178. package/dist/core/registry.js.map +1 -1
  179. package/dist/core/relevance.d.ts +74 -12
  180. package/dist/core/relevance.d.ts.map +1 -1
  181. package/dist/core/relevance.js +45 -27
  182. package/dist/core/relevance.js.map +1 -1
  183. package/dist/core/require-reason.d.ts.map +1 -1
  184. package/dist/core/require-reason.js +6 -5
  185. package/dist/core/require-reason.js.map +1 -1
  186. package/dist/core/required-fields.d.ts +28 -0
  187. package/dist/core/required-fields.d.ts.map +1 -0
  188. package/dist/core/required-fields.js +60 -0
  189. package/dist/core/required-fields.js.map +1 -0
  190. package/dist/core/revision.d.ts +82 -0
  191. package/dist/core/revision.d.ts.map +1 -0
  192. package/dist/core/revision.js +144 -0
  193. package/dist/core/revision.js.map +1 -0
  194. package/dist/core/types.d.ts +28 -2
  195. package/dist/core/types.d.ts.map +1 -1
  196. package/dist/core/types.js +12 -3
  197. package/dist/core/types.js.map +1 -1
  198. package/dist/core/workflow-core-checks.d.ts +34 -0
  199. package/dist/core/workflow-core-checks.d.ts.map +1 -0
  200. package/dist/core/workflow-core-checks.js +403 -0
  201. package/dist/core/workflow-core-checks.js.map +1 -0
  202. package/dist/core/workflow-create-remove.d.ts +65 -0
  203. package/dist/core/workflow-create-remove.d.ts.map +1 -0
  204. package/dist/core/workflow-create-remove.js +277 -0
  205. package/dist/core/workflow-create-remove.js.map +1 -0
  206. package/dist/core/workflow-deduction.d.ts +48 -0
  207. package/dist/core/workflow-deduction.d.ts.map +1 -0
  208. package/dist/core/workflow-deduction.js +523 -0
  209. package/dist/core/workflow-deduction.js.map +1 -0
  210. package/dist/core/workflow-diagnostics.d.ts +83 -0
  211. package/dist/core/workflow-diagnostics.d.ts.map +1 -0
  212. package/dist/core/workflow-diagnostics.js +375 -0
  213. package/dist/core/workflow-diagnostics.js.map +1 -0
  214. package/dist/core/workflow-exit-state.d.ts +125 -0
  215. package/dist/core/workflow-exit-state.d.ts.map +1 -0
  216. package/dist/core/workflow-exit-state.js +243 -0
  217. package/dist/core/workflow-exit-state.js.map +1 -0
  218. package/dist/core/workflow-finalize.d.ts +37 -0
  219. package/dist/core/workflow-finalize.d.ts.map +1 -0
  220. package/dist/core/workflow-finalize.js +153 -0
  221. package/dist/core/workflow-finalize.js.map +1 -0
  222. package/dist/core/workflow-list-show.d.ts +176 -0
  223. package/dist/core/workflow-list-show.d.ts.map +1 -0
  224. package/dist/core/workflow-list-show.js +268 -0
  225. package/dist/core/workflow-list-show.js.map +1 -0
  226. package/dist/core/workflow-next.d.ts +268 -0
  227. package/dist/core/workflow-next.d.ts.map +1 -0
  228. package/dist/core/workflow-next.js +503 -0
  229. package/dist/core/workflow-next.js.map +1 -0
  230. package/dist/core/workflow-registry.d.ts +52 -0
  231. package/dist/core/workflow-registry.d.ts.map +1 -0
  232. package/dist/core/workflow-registry.js +158 -0
  233. package/dist/core/workflow-registry.js.map +1 -0
  234. package/dist/core/workflow-start-end.d.ts +71 -0
  235. package/dist/core/workflow-start-end.d.ts.map +1 -0
  236. package/dist/core/workflow-start-end.js +298 -0
  237. package/dist/core/workflow-start-end.js.map +1 -0
  238. package/dist/core/workflow-status.d.ts +83 -0
  239. package/dist/core/workflow-status.d.ts.map +1 -0
  240. package/dist/core/workflow-status.js +125 -0
  241. package/dist/core/workflow-status.js.map +1 -0
  242. package/dist/core/write-guard.d.ts +51 -8
  243. package/dist/core/write-guard.d.ts.map +1 -1
  244. package/dist/core/write-guard.js +139 -9
  245. package/dist/core/write-guard.js.map +1 -1
  246. package/dist/directives/index.d.ts +3 -2
  247. package/dist/directives/index.d.ts.map +1 -1
  248. package/dist/directives/index.js +5 -3
  249. package/dist/directives/index.js.map +1 -1
  250. package/dist/directives/schema.d.ts +34 -29
  251. package/dist/directives/schema.d.ts.map +1 -1
  252. package/dist/directives/schema.js +35 -29
  253. package/dist/directives/schema.js.map +1 -1
  254. package/dist/directives/version.d.ts +28 -0
  255. package/dist/directives/version.d.ts.map +1 -0
  256. package/dist/directives/version.js +51 -0
  257. package/dist/directives/version.js.map +1 -0
  258. package/dist/dna/edit.d.ts +7 -6
  259. package/dist/dna/edit.d.ts.map +1 -1
  260. package/dist/dna/edit.js +96 -41
  261. package/dist/dna/edit.js.map +1 -1
  262. package/dist/dna/mutate.js +18 -3
  263. package/dist/dna/mutate.js.map +1 -1
  264. package/dist/dna/path.d.ts +2 -2
  265. package/dist/dna/roles.d.ts +1 -1
  266. package/dist/dna/schema.d.ts +61 -3
  267. package/dist/dna/schema.d.ts.map +1 -1
  268. package/dist/dna/schema.js +121 -6
  269. package/dist/dna/schema.js.map +1 -1
  270. package/dist/mcp/dna-resource.d.ts.map +1 -1
  271. package/dist/mcp/dna-resource.js +4 -4
  272. package/dist/mcp/dna-resource.js.map +1 -1
  273. package/dist/mcp/index.d.ts +8 -4
  274. package/dist/mcp/index.d.ts.map +1 -1
  275. package/dist/mcp/index.js +13 -2
  276. package/dist/mcp/index.js.map +1 -1
  277. package/dist/mcp/memory-resource.d.ts.map +1 -1
  278. package/dist/mcp/memory-resource.js +30 -19
  279. package/dist/mcp/memory-resource.js.map +1 -1
  280. package/dist/mcp/prompt.d.ts +61 -24
  281. package/dist/mcp/prompt.d.ts.map +1 -1
  282. package/dist/mcp/prompt.js +176 -47
  283. package/dist/mcp/prompt.js.map +1 -1
  284. package/dist/mcp/read-only.d.ts +39 -0
  285. package/dist/mcp/read-only.d.ts.map +1 -1
  286. package/dist/mcp/read-only.js +61 -0
  287. package/dist/mcp/read-only.js.map +1 -1
  288. package/dist/mcp/registrar.d.ts +10 -0
  289. package/dist/mcp/registrar.d.ts.map +1 -1
  290. package/dist/mcp/registrar.js +48 -12
  291. package/dist/mcp/registrar.js.map +1 -1
  292. package/dist/mcp/server.d.ts +21 -14
  293. package/dist/mcp/server.d.ts.map +1 -1
  294. package/dist/mcp/server.js +41 -15
  295. package/dist/mcp/server.js.map +1 -1
  296. package/dist/mcp/session-resource.d.ts +13 -0
  297. package/dist/mcp/session-resource.d.ts.map +1 -0
  298. package/dist/mcp/session-resource.js +65 -0
  299. package/dist/mcp/session-resource.js.map +1 -0
  300. package/dist/mcp/workflow-resource.d.ts +18 -0
  301. package/dist/mcp/workflow-resource.d.ts.map +1 -1
  302. package/dist/mcp/workflow-resource.js +37 -8
  303. package/dist/mcp/workflow-resource.js.map +1 -1
  304. package/dist/memory/add.d.ts +110 -24
  305. package/dist/memory/add.d.ts.map +1 -1
  306. package/dist/memory/add.js +251 -64
  307. package/dist/memory/add.js.map +1 -1
  308. package/dist/memory/audit.d.ts +152 -39
  309. package/dist/memory/audit.d.ts.map +1 -1
  310. package/dist/memory/audit.js +257 -103
  311. package/dist/memory/audit.js.map +1 -1
  312. package/dist/memory/commit-message.d.ts +60 -38
  313. package/dist/memory/commit-message.d.ts.map +1 -1
  314. package/dist/memory/commit-message.js +167 -21
  315. package/dist/memory/commit-message.js.map +1 -1
  316. package/dist/memory/entry.d.ts.map +1 -1
  317. package/dist/memory/entry.js +1 -2
  318. package/dist/memory/entry.js.map +1 -1
  319. package/dist/memory/frontmatter-edit.d.ts +16 -4
  320. package/dist/memory/frontmatter-edit.d.ts.map +1 -1
  321. package/dist/memory/frontmatter-edit.js +13 -1
  322. package/dist/memory/frontmatter-edit.js.map +1 -1
  323. package/dist/memory/git-log.d.ts +18 -6
  324. package/dist/memory/git-log.d.ts.map +1 -1
  325. package/dist/memory/git-log.js +40 -13
  326. package/dist/memory/git-log.js.map +1 -1
  327. package/dist/memory/history.d.ts +73 -12
  328. package/dist/memory/history.d.ts.map +1 -1
  329. package/dist/memory/history.js +128 -44
  330. package/dist/memory/history.js.map +1 -1
  331. package/dist/memory/index.d.ts +13 -11
  332. package/dist/memory/index.d.ts.map +1 -1
  333. package/dist/memory/index.js +34 -3
  334. package/dist/memory/index.js.map +1 -1
  335. package/dist/memory/query.d.ts +161 -19
  336. package/dist/memory/query.d.ts.map +1 -1
  337. package/dist/memory/query.js +435 -55
  338. package/dist/memory/query.js.map +1 -1
  339. package/dist/memory/schema.d.ts +52 -3
  340. package/dist/memory/schema.d.ts.map +1 -1
  341. package/dist/memory/schema.js +132 -8
  342. package/dist/memory/schema.js.map +1 -1
  343. package/dist/memory/state-machine.d.ts +65 -10
  344. package/dist/memory/state-machine.d.ts.map +1 -1
  345. package/dist/memory/state-machine.js +134 -46
  346. package/dist/memory/state-machine.js.map +1 -1
  347. package/dist/memory/submit.d.ts +52 -3
  348. package/dist/memory/submit.d.ts.map +1 -1
  349. package/dist/memory/submit.js +118 -11
  350. package/dist/memory/submit.js.map +1 -1
  351. package/dist/storage/build-stamp.d.ts +18 -0
  352. package/dist/storage/build-stamp.d.ts.map +1 -0
  353. package/dist/storage/build-stamp.js +68 -0
  354. package/dist/storage/build-stamp.js.map +1 -0
  355. package/dist/storage/builtin-adapters.d.ts +32 -0
  356. package/dist/storage/builtin-adapters.d.ts.map +1 -0
  357. package/dist/storage/builtin-adapters.js +100 -0
  358. package/dist/storage/builtin-adapters.js.map +1 -0
  359. package/dist/storage/builtin-directives.d.ts +0 -29
  360. package/dist/storage/builtin-directives.d.ts.map +1 -1
  361. package/dist/storage/builtin-directives.js +9 -4
  362. package/dist/storage/builtin-directives.js.map +1 -1
  363. package/dist/storage/commit.d.ts +310 -3
  364. package/dist/storage/commit.d.ts.map +1 -1
  365. package/dist/storage/commit.js +807 -33
  366. package/dist/storage/commit.js.map +1 -1
  367. package/dist/storage/document.d.ts +6 -0
  368. package/dist/storage/document.d.ts.map +1 -1
  369. package/dist/storage/document.js +10 -1
  370. package/dist/storage/document.js.map +1 -1
  371. package/dist/storage/dry-run.d.ts +65 -0
  372. package/dist/storage/dry-run.d.ts.map +1 -0
  373. package/dist/storage/dry-run.js +150 -0
  374. package/dist/storage/dry-run.js.map +1 -0
  375. package/dist/storage/errors.d.ts +26 -0
  376. package/dist/storage/errors.d.ts.map +1 -1
  377. package/dist/storage/errors.js +27 -1
  378. package/dist/storage/errors.js.map +1 -1
  379. package/dist/storage/frontmatter.d.ts +1 -1
  380. package/dist/storage/frontmatter.js +1 -1
  381. package/dist/storage/git-read.d.ts +64 -0
  382. package/dist/storage/git-read.d.ts.map +1 -0
  383. package/dist/storage/git-read.js +107 -0
  384. package/dist/storage/git-read.js.map +1 -0
  385. package/dist/storage/index.d.ts +13 -5
  386. package/dist/storage/index.d.ts.map +1 -1
  387. package/dist/storage/index.js +39 -2
  388. package/dist/storage/index.js.map +1 -1
  389. package/dist/storage/layout.d.ts +2 -2
  390. package/dist/storage/layout.d.ts.map +1 -1
  391. package/dist/storage/layout.js +2 -7
  392. package/dist/storage/layout.js.map +1 -1
  393. package/dist/storage/memory-path.d.ts +18 -5
  394. package/dist/storage/memory-path.d.ts.map +1 -1
  395. package/dist/storage/memory-path.js +41 -5
  396. package/dist/storage/memory-path.js.map +1 -1
  397. package/dist/storage/snapshot.js +1 -1
  398. package/dist/storage/templates.d.ts +33 -6
  399. package/dist/storage/templates.d.ts.map +1 -1
  400. package/dist/storage/templates.js +170 -16
  401. package/dist/storage/templates.js.map +1 -1
  402. package/dist/validation/config-version.d.ts +19 -0
  403. package/dist/validation/config-version.d.ts.map +1 -0
  404. package/dist/validation/config-version.js +53 -0
  405. package/dist/validation/config-version.js.map +1 -0
  406. package/dist/validation/diagnostic.d.ts +40 -0
  407. package/dist/validation/diagnostic.d.ts.map +1 -0
  408. package/dist/validation/diagnostic.js +41 -0
  409. package/dist/validation/diagnostic.js.map +1 -0
  410. package/dist/validation/format.d.ts +74 -0
  411. package/dist/validation/format.d.ts.map +1 -0
  412. package/dist/validation/format.js +104 -0
  413. package/dist/validation/format.js.map +1 -0
  414. package/dist/validation/id.d.ts +14 -0
  415. package/dist/validation/id.d.ts.map +1 -1
  416. package/dist/validation/id.js +35 -11
  417. package/dist/validation/id.js.map +1 -1
  418. package/dist/validation/identity.d.ts +40 -0
  419. package/dist/validation/identity.d.ts.map +1 -0
  420. package/dist/validation/identity.js +78 -0
  421. package/dist/validation/identity.js.map +1 -0
  422. package/dist/validation/index.d.ts +9 -3
  423. package/dist/validation/index.d.ts.map +1 -1
  424. package/dist/validation/index.js +30 -1
  425. package/dist/validation/index.js.map +1 -1
  426. package/dist/validation/secret-scan.d.ts +3 -2
  427. package/dist/validation/secret-scan.d.ts.map +1 -1
  428. package/dist/validation/secret-scan.js +32 -11
  429. package/dist/validation/secret-scan.js.map +1 -1
  430. package/dist/validation/warning.d.ts +30 -16
  431. package/dist/validation/warning.d.ts.map +1 -1
  432. package/dist/validation/warning.js +74 -7
  433. package/dist/validation/warning.js.map +1 -1
  434. package/dist/workflow/bindings.d.ts +152 -0
  435. package/dist/workflow/bindings.d.ts.map +1 -0
  436. package/dist/workflow/bindings.js +249 -0
  437. package/dist/workflow/bindings.js.map +1 -0
  438. package/dist/workflow/create.d.ts +13 -0
  439. package/dist/workflow/create.d.ts.map +1 -0
  440. package/dist/workflow/create.js +41 -0
  441. package/dist/workflow/create.js.map +1 -0
  442. package/dist/workflow/deduce.d.ts +400 -0
  443. package/dist/workflow/deduce.d.ts.map +1 -0
  444. package/dist/workflow/deduce.js +1109 -0
  445. package/dist/workflow/deduce.js.map +1 -0
  446. package/dist/workflow/manifest-edit.d.ts +19 -0
  447. package/dist/workflow/manifest-edit.d.ts.map +1 -0
  448. package/dist/workflow/manifest-edit.js +124 -0
  449. package/dist/workflow/manifest-edit.js.map +1 -0
  450. package/dist/workflow/schema.d.ts +158 -19
  451. package/dist/workflow/schema.d.ts.map +1 -1
  452. package/dist/workflow/schema.js +163 -15
  453. package/dist/workflow/schema.js.map +1 -1
  454. package/package.json +18 -8
@@ -1,15 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ROLES_YAML_PATH = exports.DIRECTIVES_DIR_PATH = exports.DNA_YAML_PATH = exports.MEMORY_YAML_PATH = void 0;
3
+ exports.ROLES_YAML_PATH = exports.DIRECTIVES_DIR_PATH = exports.DNA_YAML_PATH = exports.MEMORY_YAML_PATH = exports.ConfigFileMissingError = void 0;
4
4
  exports.loadMemoryYaml = loadMemoryYaml;
5
5
  exports.loadMemoryYamlAtHead = loadMemoryYamlAtHead;
6
+ exports.loadMemoryYamlAtRev = loadMemoryYamlAtRev;
7
+ exports.loadMemoryYamlAtCommit = loadMemoryYamlAtCommit;
6
8
  exports.loadDnaYaml = loadDnaYaml;
7
9
  exports.loadDnaYamlAtHead = loadDnaYamlAtHead;
10
+ exports.loadDnaYamlAtRev = loadDnaYamlAtRev;
11
+ exports.loadDnaYamlAtCommit = loadDnaYamlAtCommit;
8
12
  exports.loadWorkflowsYaml = loadWorkflowsYaml;
13
+ exports.loadWorkflowsYamlAtRev = loadWorkflowsYamlAtRev;
14
+ exports.loadWorkflowsYamlAtCommit = loadWorkflowsYamlAtCommit;
15
+ exports.loadDirectiveInventory = loadDirectiveInventory;
9
16
  exports.loadDirectives = loadDirectives;
10
17
  exports.loadDirectivesAtHead = loadDirectivesAtHead;
18
+ exports.loadDirectivesAtRev = loadDirectivesAtRev;
19
+ exports.loadDirectivesAtCommit = loadDirectivesAtCommit;
11
20
  exports.loadRolesYaml = loadRolesYaml;
12
21
  exports.loadRolesYamlAtHead = loadRolesYamlAtHead;
22
+ exports.loadRolesYamlAtRev = loadRolesYamlAtRev;
23
+ exports.loadRolesYamlAtCommit = loadRolesYamlAtCommit;
13
24
  /**
14
25
  * Per-pillar loaders (task-004-decoupled-pillars, REQ-SYS-02). `core` exposes exactly one loader
15
26
  * per pillar — `loadMemoryYaml`, `loadDnaYaml`, `loadWorkflowsYaml`, `loadDirectives` — each reading
@@ -24,40 +35,163 @@ exports.loadRolesYamlAtHead = loadRolesYamlAtHead;
24
35
  const fs_1 = require("fs");
25
36
  const path_1 = require("path");
26
37
  const schema_1 = require("../directives/schema");
38
+ const version_1 = require("../directives/version");
27
39
  const schema_2 = require("../dna/schema");
28
40
  const schema_3 = require("../memory/schema");
29
41
  const storage_1 = require("../storage");
30
42
  const validation_1 = require("../validation");
43
+ const format_1 = require("../validation/format");
31
44
  const schema_4 = require("../workflow/schema");
45
+ const revision_1 = require("./revision");
46
+ const bindings_1 = require("../workflow/bindings");
47
+ const workflow_diagnostics_1 = require("./workflow-diagnostics");
32
48
  /**
33
- * Recursively list every `.md` file under `dir`, as paths relative to `dir`, sorted
34
- * lexicographically (REQ-SYS-07: no unordered iteration in a context-building path — directory-entry
49
+ * The resolved `Stats` of `full`, or why it cannot be resolved (task-143, `bug-125`). `statSync`
50
+ * follows a symbolic link, so a dangling one throws `ENOENT` — the raw error that used to escape every
51
+ * directive read. `lstatSync` sees the entry itself first, so a broken link is told apart from any
52
+ * other failure and named as one. The reasons are phrased like `requireInspectableTarget`'s
53
+ * (`./write-guard.ts`), the write-side guard over the same kind of entry.
54
+ */
55
+ function resolveEntry(full) {
56
+ try {
57
+ return (0, fs_1.statSync)(full);
58
+ }
59
+ catch (error) {
60
+ const code = error.code;
61
+ let isLink = false;
62
+ try {
63
+ isLink = (0, fs_1.lstatSync)(full).isSymbolicLink();
64
+ }
65
+ catch {
66
+ // `lstat` fails too — a directory listed without search permission, or an entry that vanished
67
+ // after `readdirSync`: report what `stat` said.
68
+ }
69
+ if (isLink && (code === 'ENOENT' || code === 'ENOTDIR')) {
70
+ return { reason: 'it is a symbolic link whose target does not exist' };
71
+ }
72
+ return { reason: `it cannot be read (${String(code)})` };
73
+ }
74
+ }
75
+ /** The `code` of a filesystem error, as a skip reason's suffix. */
76
+ function cannotBeRead(error) {
77
+ return `it cannot be read (${String(error.code)})`;
78
+ }
79
+ /**
80
+ * Recursively list every `.md` file under `dir`, as paths relative to `dir`, in lexicographic
81
+ * traversal order (REQ-SYS-07: no unordered iteration in a context-building path — directory-entry
35
82
  * order from `readdirSync` is not guaranteed stable, so traversal always sorts explicitly, mirroring
36
- * `src/storage/snapshot.ts`'s `listFilesSorted`). Returns `[]` if `dir` doesn't exist.
83
+ * `src/storage/snapshot.ts`'s `listFilesSorted`). Returns no entries if `dir` doesn't exist.
84
+ *
85
+ * An entry that cannot be resolved — a dangling symbolic link above all, a subdirectory that cannot be
86
+ * listed, or a directory link back to one of its own ancestors — is **skipped**, and comes back as a
87
+ * `skipped` entry with its reason, in the same traversal order (task-143, `bug-125`). Whatever the
88
+ * link's name, it is reported: a dangling link has no type, so one named without `.md` may have been
89
+ * a directory of directives. `dir` itself, when it is a dangling link, is one skipped entry with
90
+ * `relative: ''`.
91
+ *
92
+ * Symlinked directories are followed, but never into a directory already on the walk's stack (by
93
+ * `realpath`): without that, a link to an ancestor is walked until the OS refuses (ELOOP after some
94
+ * forty levels), and every directive above it is listed that many times (task-143 review).
95
+ *
96
+ * `dir` itself is the pillar: when it cannot be listed, `rootError` carries the reason and the caller
97
+ * refuses the read.
37
98
  */
38
99
  function listMarkdownFilesSorted(dir) {
39
- if (!(0, fs_1.existsSync)(dir) || !(0, fs_1.statSync)(dir).isDirectory())
40
- return [];
41
- const out = [];
42
- const walk = (current, prefix) => {
43
- for (const entry of (0, fs_1.readdirSync)(current).sort()) {
100
+ const entries = [];
101
+ if (!(0, fs_1.existsSync)(dir)) {
102
+ // `existsSync` follows a link: a dangling `dir` is reported, a genuinely absent one is not.
103
+ // `lstat` itself fails on a path through a non-directory (`.wingfoil` a file): absent, as before.
104
+ let isLink;
105
+ try {
106
+ isLink = (0, fs_1.lstatSync)(dir).isSymbolicLink();
107
+ }
108
+ catch {
109
+ isLink = false;
110
+ }
111
+ if (isLink)
112
+ entries.push({ relative: '', kind: 'skipped', reason: 'it is a symbolic link whose target does not exist' });
113
+ return { entries };
114
+ }
115
+ if (!(0, fs_1.statSync)(dir).isDirectory())
116
+ return { entries };
117
+ let rootNames;
118
+ try {
119
+ rootNames = (0, fs_1.readdirSync)(dir);
120
+ }
121
+ catch (error) {
122
+ return { entries, rootError: cannotBeRead(error) };
123
+ }
124
+ const walk = (current, prefix, names, ancestors) => {
125
+ for (const entry of [...names].sort()) {
44
126
  const full = (0, path_1.join)(current, entry);
45
127
  const relative = prefix ? `${prefix}/${entry}` : entry;
46
- if ((0, fs_1.statSync)(full).isDirectory()) {
47
- walk(full, relative);
128
+ const resolved = resolveEntry(full);
129
+ if (!('isDirectory' in resolved)) {
130
+ entries.push({ relative, kind: 'skipped', reason: resolved.reason });
131
+ }
132
+ else if (resolved.isDirectory()) {
133
+ let real;
134
+ let children;
135
+ try {
136
+ real = (0, fs_1.realpathSync)(full);
137
+ children = (0, fs_1.readdirSync)(full);
138
+ }
139
+ catch (error) {
140
+ entries.push({ relative, kind: 'skipped', reason: cannotBeRead(error) });
141
+ continue;
142
+ }
143
+ if (ancestors.has(real)) {
144
+ entries.push({ relative, kind: 'skipped', reason: 'it is a symbolic link to an ancestor directory' });
145
+ continue;
146
+ }
147
+ walk(full, relative, children, new Set([...ancestors, real]));
48
148
  }
49
149
  else if (entry.endsWith('.md')) {
50
- out.push(relative);
150
+ entries.push({ relative, kind: 'file' });
51
151
  }
52
152
  }
53
153
  };
54
- walk(dir, '');
55
- return out.sort();
154
+ walk(dir, '', rootNames, new Set([(0, fs_1.realpathSync)(dir)]));
155
+ return { entries };
156
+ }
157
+ /**
158
+ * A configuration file the read needs is not in the working tree (task-179, `bug-245`): a `.wingfoil/`
159
+ * exists — the absent case is `requireInitializedProject`'s — but this pillar's file does not. The
160
+ * message names the file repository-relative and what to do, never the host's absolute path, so the
161
+ * output is the same on every machine and safe to paste into an issue. It keeps Node's `code: 'ENOENT'`,
162
+ * so every caller that maps a missing file (`coreErrorOf` → `NOT_FOUND`, exit `1`) still does.
163
+ */
164
+ class ConfigFileMissingError extends Error {
165
+ relativePath;
166
+ /** Node's code for a missing file, kept so existing `ENOENT` mappings still apply. */
167
+ code = 'ENOENT';
168
+ /** @param relativePath - the missing file, relative to the repository root (`.wingfoil/dna.yaml`). */
169
+ constructor(relativePath) {
170
+ super(`${relativePath} is missing: restore it from git, or re-create it ('wingfoil init' scaffolds a complete .wingfoil/ in a project that has none)`);
171
+ this.relativePath = relativePath;
172
+ this.name = 'ConfigFileMissingError';
173
+ }
174
+ }
175
+ exports.ConfigFileMissingError = ConfigFileMissingError;
176
+ /**
177
+ * Read a working-tree configuration file named repository-relative (`.wingfoil/dna.yaml`): a missing one
178
+ * is a {@link ConfigFileMissingError}, any other failure propagates unchanged. The relative path is also
179
+ * the label every parse and validation issue carries (task-179, `bug-245`), as the `HEAD:`-labelled
180
+ * readers already do.
181
+ */
182
+ function readConfigFile(root, relativePath) {
183
+ try {
184
+ return (0, storage_1.readDocument)((0, path_1.join)(root, relativePath));
185
+ }
186
+ catch (error) {
187
+ if (error?.code === 'ENOENT')
188
+ throw new ConfigFileMissingError(relativePath);
189
+ throw error;
190
+ }
56
191
  }
57
192
  /** Load and validate `.wingfoil/memory.yaml` in isolation (spec-001-memory-yaml-schema). */
58
193
  function loadMemoryYaml(root) {
59
- const filePath = (0, path_1.join)(root, '.wingfoil', 'memory.yaml');
60
- return parseMemoryYaml((0, storage_1.readDocument)(filePath), filePath);
194
+ return parseMemoryYaml(readConfigFile(root, exports.MEMORY_YAML_PATH), exports.MEMORY_YAML_PATH);
61
195
  }
62
196
  /**
63
197
  * Root-relative POSIX path of `memory.yaml` — the form git wants for a revision read
@@ -69,10 +203,13 @@ exports.MEMORY_YAML_PATH = '.wingfoil/memory.yaml';
69
203
  * The Memory pillar's two-pass parse, over bytes that may come from anywhere — the working-tree file
70
204
  * ({@link loadMemoryYaml}) or a git revision ({@link loadMemoryYamlAtHead}). `filePath` is a label
71
205
  * only: it rides every issue this raises, so an error names the baseline it came from
72
- * (`HEAD:.wingfoil/memory.yaml`, not just a path on disk).
206
+ * (`HEAD:.wingfoil/memory.yaml`, not just a path on disk). A `format` newer than this build reads is
207
+ * refused before the structural pass (`dl-149`, task-251).
73
208
  */
74
209
  function parseMemoryYaml(raw, filePath) {
75
- return (0, validation_1.runValidation)(schema_3.MemoryYaml, (0, validation_1.parseYaml)(raw, filePath), filePath);
210
+ const data = (0, validation_1.parseYaml)(raw, filePath);
211
+ (0, format_1.refuseNewerFormat)(data, format_1.MEMORY_YAML_FORMAT, filePath);
212
+ return (0, validation_1.runValidation)(schema_3.MemoryYaml, data, filePath);
76
213
  }
77
214
  /**
78
215
  * Load and validate `.wingfoil/memory.yaml` **as the repository has committed it** — the version at
@@ -94,10 +231,34 @@ function parseMemoryYaml(raw, filePath) {
94
231
  * byte-identical, and `HEAD` is the one available before that commit exists.
95
232
  */
96
233
  function loadMemoryYamlAtHead(root) {
97
- const raw = (0, storage_1.readPathAtRev)(root, 'HEAD', exports.MEMORY_YAML_PATH);
234
+ return (0, revision_1.atHeadOr)(root, () => loadMemoryYamlAtRev(root, 'HEAD'), null);
235
+ }
236
+ /**
237
+ * Load and validate `.wingfoil/memory.yaml` **as commit `rev` holds it** (task-137, `spec-012` §2
238
+ * `stateRef`) — the general form of {@link loadMemoryYamlAtHead}, which is this at `'HEAD'`. `null`
239
+ * when that commit does not hold the file; a later commit and the working tree are never read.
240
+ *
241
+ * @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
242
+ * @throws `RevisionError` when `rev` is malformed or names no commit — never an empty answer.
243
+ */
244
+ function loadMemoryYamlAtRev(root, rev) {
245
+ return memoryYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
246
+ }
247
+ /**
248
+ * {@link loadMemoryYamlAtRev} for a `sha` already resolved to a commit (`resolveRevision`, `./revision.ts`),
249
+ * read without resolving it again: a reader that reads several pillars at one commit resolves it once
250
+ * (task-283, `bug-309`: the workflow deduction's snapshot).
251
+ *
252
+ * @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
253
+ */
254
+ function loadMemoryYamlAtCommit(root, sha) {
255
+ return memoryYamlAt(root, sha, sha);
256
+ }
257
+ function memoryYamlAt(root, sha, label) {
258
+ const raw = (0, storage_1.readPathAtRev)(root, sha, exports.MEMORY_YAML_PATH);
98
259
  if (raw === null)
99
260
  return null;
100
- return parseMemoryYaml(raw, `HEAD:${exports.MEMORY_YAML_PATH}`);
261
+ return parseMemoryYaml(raw, `${label}:${exports.MEMORY_YAML_PATH}`);
101
262
  }
102
263
  /**
103
264
  * `js-yaml`'s `YAMLException#message` always embeds the failure position as `(<line>:<column>)`
@@ -121,7 +282,7 @@ function extractYamlErrorLine(message) {
121
282
  * `parseYaml`/`ValidationError`'s shared behavior for `memory.yaml`/`workflows.yaml`/directives.
122
283
  */
123
284
  function loadDnaYaml(root) {
124
- return parseDnaYaml((0, storage_1.readDocument)((0, path_1.join)(root, '.wingfoil', 'dna.yaml')), (0, path_1.join)(root, '.wingfoil', 'dna.yaml'));
285
+ return parseDnaYaml(readConfigFile(root, exports.DNA_YAML_PATH), exports.DNA_YAML_PATH);
125
286
  }
126
287
  /**
127
288
  * Root-relative POSIX path of `dna.yaml` — the form git wants for a revision read
@@ -147,6 +308,8 @@ function parseDnaYaml(raw, filePath) {
147
308
  }
148
309
  throw err;
149
310
  }
311
+ // A file from a newer WingFoil is refused for its format alone (`dl-149`, task-251).
312
+ (0, format_1.refuseNewerFormat)(data, format_1.DNA_YAML_FORMAT, filePath);
150
313
  return (0, validation_1.runValidation)(schema_2.DnaYaml, data, filePath);
151
314
  }
152
315
  /**
@@ -177,55 +340,263 @@ function parseDnaYaml(raw, filePath) {
177
340
  * a write refuses while its target carries modifications it does not own.)
178
341
  */
179
342
  function loadDnaYamlAtHead(root) {
180
- const raw = (0, storage_1.readPathAtRev)(root, 'HEAD', exports.DNA_YAML_PATH);
343
+ return (0, revision_1.atHeadOr)(root, () => loadDnaYamlAtRev(root, 'HEAD'), null);
344
+ }
345
+ /**
346
+ * Load and validate `.wingfoil/dna.yaml` **as commit `rev` holds it** (task-137, `spec-012` §2
347
+ * `stateRef`) — the general form of {@link loadDnaYamlAtHead}, which is this at `'HEAD'`. `null` when
348
+ * that commit does not hold the file; a later commit and the working tree are never read.
349
+ *
350
+ * @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
351
+ * @throws `RevisionError` when `rev` is malformed or names no commit — never an empty answer.
352
+ */
353
+ function loadDnaYamlAtRev(root, rev) {
354
+ return dnaYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
355
+ }
356
+ /**
357
+ * {@link loadDnaYamlAtRev} for a `sha` already resolved to a commit, read without resolving it again
358
+ * (task-283, `bug-309`).
359
+ *
360
+ * @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
361
+ */
362
+ function loadDnaYamlAtCommit(root, sha) {
363
+ return dnaYamlAt(root, sha, sha);
364
+ }
365
+ function dnaYamlAt(root, sha, label) {
366
+ const raw = (0, storage_1.readPathAtRev)(root, sha, exports.DNA_YAML_PATH);
181
367
  if (raw === null)
182
368
  return null;
183
- return parseDnaYaml(raw, `HEAD:${exports.DNA_YAML_PATH}`);
369
+ return parseDnaYaml(raw, `${label}:${exports.DNA_YAML_PATH}`);
370
+ }
371
+ /** The manifest's path relative to `.wingfoil/` — the `file` of its diagnostics (spec-003). */
372
+ const WORKFLOWS_MANIFEST_FILE = 'workflows.yaml';
373
+ /** A Zod issue path as spec-003 spells a diagnostic `path`: `phases[3].include`. */
374
+ function diagnosticPath(path) {
375
+ let out = '';
376
+ for (const segment of path) {
377
+ if (typeof segment === 'number')
378
+ out += `[${segment}]`;
379
+ else
380
+ out += out === '' ? String(segment) : `.${String(segment)}`;
381
+ }
382
+ return out;
383
+ }
384
+ /**
385
+ * One included file's structural (Zod) pass, as diagnostics (spec-003 § "Diagnostics": structural
386
+ * failures keep spec-009's structural codes, except the named `kind` refusal). An empty list means the
387
+ * file is valid and `workflow` is set.
388
+ */
389
+ function parseWorkflowFile(data, file, filePath) {
390
+ const result = schema_4.Workflow.safeParse(data);
391
+ if (result.success) {
392
+ // The same once-per-file unknown-field warning `runValidation` fires (spec-009 §2).
393
+ (0, validation_1.emitUnknownFieldWarning)(data, schema_4.Workflow, filePath);
394
+ return { workflow: result.data, diagnostics: [] };
395
+ }
396
+ const diagnostics = zodDiagnostics(result.error.issues, file).map((diagnostic) => diagnostic.path === 'kind' ? { ...diagnostic, code: 'E_WORKFLOW_INVALID_KIND' } : diagnostic);
397
+ return { workflow: null, diagnostics };
398
+ }
399
+ /** Zod issues as `E_VALIDATION` diagnostics of `file` (spec-009's structural code). */
400
+ function zodDiagnostics(issues, file) {
401
+ return issues.map((issue) => ({ code: validation_1.E_VALIDATION, severity: 'error', file, path: diagnosticPath(issue.path), message: issue.message }));
402
+ }
403
+ /**
404
+ * Parse `raw` as YAML; a parse failure becomes one `E_YAML_PARSE_ERROR` diagnostic of `file` (the
405
+ * spec-009 code, with the parser's message) instead of a throw, so it takes its place in the array.
406
+ */
407
+ function parseYamlOrDiagnostic(raw, filePath, file) {
408
+ try {
409
+ return { data: (0, validation_1.parseYaml)(raw, filePath), diagnostic: null };
410
+ }
411
+ catch (err) {
412
+ // `parseYaml` throws nothing but `ValidationError.yamlParse`: one issue, the parser's message.
413
+ const { message } = err.issues[0];
414
+ return { data: null, diagnostic: { code: validation_1.E_YAML_PARSE_ERROR, severity: 'error', file, path: '', message } };
415
+ }
184
416
  }
185
417
  /**
186
418
  * Load and validate the Workflow pillar in isolation (spec-003-workflows-yaml-schema): Layer 1
187
419
  * (`.wingfoil/workflows.yaml`), then every file its `include` list names (Layer 2), resolved
188
- * relative to `.wingfoil/` per spec-003. Two cross-file Pass-2 checks that need the loaded files
189
- * themselves (spec-009 §1 "Cross-file" category) run here, not in the schema module:
420
+ * relative to `.wingfoil/` per spec-003, then the **loader** rows of spec-003 § "Diagnostics" — the
421
+ * checks that need the loaded files themselves (spec-009 §1 "Cross-file" category), run here rather
422
+ * than in the schema module (`./workflow-diagnostics.ts`, task-136).
190
423
  *
191
- * - every `include` path must resolve to a file that actually exists
192
- * (`E_WORKFLOW_FILE_NOT_FOUND`, spec-003);
193
- * - at least one loaded workflow must be `kind: main` (REQ-STATE-03 / spec-003 Layer 1 step 3).
424
+ * - An **absent** manifest is an empty registry, with no diagnostic (spec-003 Layer 1).
425
+ * - A manifest that is not YAML, or fails its structural pass, is the whole array: one
426
+ * `E_YAML_PARSE_ERROR`, or its Zod issues as `E_VALIDATION`, on `workflows.yaml`.
427
+ * - Everything else is collected into one ordered `diagnostics` array (REQ-SYS-07): the manifest's
428
+ * (`E_WORKFLOW_FILE_NOT_FOUND`, `E_NO_MAIN_WORKFLOW`), then each file in `include` order —
429
+ * a YAML parse failure (`E_YAML_PARSE_ERROR`) or the structural (Zod) issues, then
430
+ * workflow-level, then phase-level. If it holds an error, the load throws a
431
+ * {@link DiagnosticsError}: `VALIDATION`, exit `1`, the first error as the reason and the whole array
432
+ * attached.
194
433
  */
195
434
  function loadWorkflowsYaml(root) {
196
435
  const configDir = (0, path_1.join)(root, '.wingfoil');
197
- const manifestPath = (0, path_1.join)(configDir, 'workflows.yaml');
198
- const manifestRaw = (0, storage_1.readDocument)(manifestPath);
199
- const manifestData = (0, validation_1.parseYaml)(manifestRaw, manifestPath);
200
- const manifest = (0, validation_1.runValidation)(schema_4.WorkflowsYaml, manifestData, manifestPath);
201
- const workflows = [];
202
- for (const includePath of manifest.include) {
203
- const workflowPath = (0, path_1.join)(configDir, includePath);
204
- if (!(0, storage_1.documentExists)(workflowPath)) {
205
- throw validation_1.ValidationError.semantic([
206
- {
207
- code: 'E_WORKFLOW_FILE_NOT_FOUND',
208
- path: 'include',
209
- file: manifestPath,
210
- message: `included workflow file not found: ${includePath}`,
211
- },
212
- ]);
436
+ return loadWorkflowsFrom({
437
+ read: (file) => {
438
+ const path = (0, path_1.join)(configDir, file);
439
+ return (0, storage_1.documentExists)(path) ? (0, storage_1.readDocument)(path) : null;
440
+ },
441
+ // Repository-relative, never the host path (task-179, `bug-245`).
442
+ label: (file) => `.wingfoil/${file}`,
443
+ });
444
+ }
445
+ /**
446
+ * Load and validate the Workflow pillar **as commit `rev` holds it** (task-137, `spec-017` §1.1: the
447
+ * registry is read at `HEAD`): the manifest and every file it includes, read at that commit. It runs
448
+ * the same body as {@link loadWorkflowsYaml} over the committed bytes, so the result — or the
449
+ * {@link DiagnosticsError} and its ordered `diagnostics` array (task-136) — is the one the working-tree
450
+ * loader gives for the same bytes. Only the parse-error labels differ (`<rev>:.wingfoil/<file>`); every
451
+ * diagnostic's `file` stays relative to `.wingfoil/`. A path the commit holds as a directory counts as
452
+ * absent (`E_WORKFLOW_FILE_NOT_FOUND`).
453
+ *
454
+ * @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
455
+ * @param overlay - Files that replace the commit's own, keyed by repository-relative path (`.wingfoil/…`);
456
+ * `null` reads as absent. `workflow create` / `remove` (task-211) load the registry as their commit would
457
+ * leave it, before writing it. Empty by default: the commit alone.
458
+ * @throws `RevisionError` when `rev` is malformed or names no commit — never an empty registry.
459
+ */
460
+ function loadWorkflowsYamlAtRev(root, rev, overlay = new Map()) {
461
+ return workflowsYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev, overlay);
462
+ }
463
+ /**
464
+ * {@link loadWorkflowsYamlAtRev} for a `sha` already resolved to a commit, read without resolving it
465
+ * again (task-283, `bug-309`).
466
+ *
467
+ * @param sha - The full sha of a commit, as `resolveRevision` returned it; labels name it.
468
+ * @param overlay - As {@link loadWorkflowsYamlAtRev}'s.
469
+ */
470
+ function loadWorkflowsYamlAtCommit(root, sha, overlay = new Map()) {
471
+ return workflowsYamlAt(root, sha, sha, overlay);
472
+ }
473
+ function workflowsYamlAt(root, sha, rev, overlay) {
474
+ const committedPath = (file) => path_1.posix.normalize(`.wingfoil/${file}`);
475
+ // The included files and `bindings.yaml`, read in one `cat-file --batch` once the manifest names them
476
+ // (task-216, REQ-PERF-03: one spawn instead of one per workflow file).
477
+ const fetched = new Map();
478
+ const fetch = (files) => {
479
+ const answers = (0, storage_1.readPathsAtRev)(root, sha, files.map(committedPath));
480
+ return files.map((file, index) => {
481
+ const path = committedPath(file);
482
+ return overlay.has(path) ? overlay.get(path) : (answers[index] ?? null);
483
+ });
484
+ };
485
+ return loadWorkflowsFrom({
486
+ read: (file) => {
487
+ if (fetched.has(file))
488
+ return fetched.get(file);
489
+ const [raw = null] = fetch([file]);
490
+ return raw;
491
+ },
492
+ prefetch: (files) => {
493
+ const answers = fetch(files);
494
+ files.forEach((file, index) => fetched.set(file, answers[index] ?? null));
495
+ },
496
+ label: (file) => `${rev}:${committedPath(file)}`,
497
+ });
498
+ }
499
+ /**
500
+ * The Workflow pillar's load (spec-003 Layers 1–2 and the loader rows of § "Diagnostics"), over a
501
+ * {@link WorkflowSource} — the one body {@link loadWorkflowsYaml} and {@link loadWorkflowsYamlAtRev}
502
+ * share, so the two baselines cannot drift on what a registry or a diagnostic is.
503
+ */
504
+ function loadWorkflowsFrom(source) {
505
+ const manifestRaw = source.read(WORKFLOWS_MANIFEST_FILE);
506
+ if (manifestRaw === null)
507
+ return { manifest: null, workflows: [], bindings: null, diagnostics: [] };
508
+ const manifestPath = source.label(WORKFLOWS_MANIFEST_FILE);
509
+ // A manifest that cannot be read as YAML, or fails its structural pass, names no file to load: its
510
+ // diagnostics are the whole array.
511
+ const manifestData = parseYamlOrDiagnostic(manifestRaw, manifestPath, WORKFLOWS_MANIFEST_FILE);
512
+ if (manifestData.diagnostic)
513
+ throw new validation_1.DiagnosticsError([manifestData.diagnostic]);
514
+ // A manifest from a newer WingFoil is refused for its format alone (`dl-149`, task-251).
515
+ const manifestFormat = (0, format_1.newerFormatIssue)(manifestData.data, format_1.WORKFLOWS_YAML_FORMAT, WORKFLOWS_MANIFEST_FILE);
516
+ if (manifestFormat)
517
+ throw new validation_1.DiagnosticsError([{ ...manifestFormat, severity: 'error' }]);
518
+ const manifestResult = schema_4.WorkflowsYaml.safeParse(manifestData.data);
519
+ if (!manifestResult.success)
520
+ throw new validation_1.DiagnosticsError(zodDiagnostics(manifestResult.error.issues, WORKFLOWS_MANIFEST_FILE));
521
+ (0, validation_1.emitUnknownFieldWarning)(manifestData.data, schema_4.WorkflowsYaml, manifestPath);
522
+ const manifest = manifestResult.data;
523
+ source.prefetch?.([...manifest.include, bindings_1.BINDINGS_FILE]);
524
+ const manifestDiagnostics = [];
525
+ const files = [];
526
+ const structural = [];
527
+ manifest.include.forEach((includePath, position) => {
528
+ const raw = source.read(includePath);
529
+ if (raw === null) {
530
+ manifestDiagnostics.push({
531
+ code: 'E_WORKFLOW_FILE_NOT_FOUND',
532
+ severity: 'error',
533
+ file: WORKFLOWS_MANIFEST_FILE,
534
+ path: `include[${position}]`,
535
+ message: `included workflow file not found: ${includePath}`,
536
+ });
537
+ return;
213
538
  }
214
- const raw = (0, storage_1.readDocument)(workflowPath);
215
- const data = (0, validation_1.parseYaml)(raw, workflowPath);
216
- workflows.push((0, validation_1.runValidation)(schema_4.Workflow, data, workflowPath));
217
- }
218
- if (!workflows.some((workflow) => workflow.kind === 'main')) {
219
- throw validation_1.ValidationError.semantic([
220
- {
221
- code: 'E_NO_MAIN_WORKFLOW',
222
- path: 'include',
223
- file: manifestPath,
224
- message: 'at least one included workflow must be `kind: main` (REQ-STATE-03)',
225
- },
226
- ]);
227
- }
228
- return { manifest, workflows };
539
+ const workflowPath = source.label(includePath);
540
+ const yaml = parseYamlOrDiagnostic(raw, workflowPath, includePath);
541
+ if (yaml.diagnostic) {
542
+ files.push({ file: includePath, workflow: null, rawName: null });
543
+ structural.push([yaml.diagnostic]);
544
+ return;
545
+ }
546
+ const data = yaml.data;
547
+ const rawName = data?.name;
548
+ // A workflow file from a newer WingFoil: its format is its one structural diagnostic (task-251).
549
+ const newerFormat = (0, format_1.newerFormatIssue)(data, format_1.WORKFLOW_FORMAT, includePath);
550
+ if (newerFormat) {
551
+ files.push({ file: includePath, workflow: null, rawName: typeof rawName === 'string' ? rawName : null });
552
+ structural.push([{ ...newerFormat, severity: 'error' }]);
553
+ return;
554
+ }
555
+ const parsed = parseWorkflowFile(data, includePath, workflowPath);
556
+ files.push({ file: includePath, workflow: parsed.workflow, rawName: typeof rawName === 'string' ? rawName : null });
557
+ structural.push(parsed.diagnostics);
558
+ });
559
+ const index = (0, workflow_diagnostics_1.indexWorkflowFiles)(files, manifest.include.length - files.length);
560
+ const noStartable = (0, workflow_diagnostics_1.noStartableDiagnostic)(files, index);
561
+ if (noStartable)
562
+ manifestDiagnostics.push(noStartable);
563
+ // `workflows/bindings.yaml` (Layer 3, task-175) is read before the workflow files are checked, since
564
+ // their tokens resolve through it, but its own diagnostics come last (spec-003 § "Diagnostics" order).
565
+ const layer3 = loadBindingsFrom(source);
566
+ const diagnostics = [...manifestDiagnostics];
567
+ files.forEach((_, i) => {
568
+ diagnostics.push(...structural[i], ...(0, workflow_diagnostics_1.workflowFileDiagnostics)(files, index, i, layer3.context));
569
+ });
570
+ diagnostics.push(...layer3.diagnostics);
571
+ if (diagnostics.some((diagnostic) => diagnostic.severity === 'error'))
572
+ throw new validation_1.DiagnosticsError(diagnostics);
573
+ // Every file is structurally valid once no error was reported.
574
+ return { manifest, workflows: files.map((loaded) => loaded.workflow), bindings: layer3.context.bindings, diagnostics };
575
+ }
576
+ /**
577
+ * Layer 3 (spec-003, task-175): `workflows/bindings.yaml` through the same source as the workflow
578
+ * files. Absent → no bindings and no diagnostic. Not YAML → one `E_YAML_PARSE_ERROR`; a newer `format`
579
+ * (`dl-153` (A)) → one `E_INVALID_FORMAT` in place of the structural pass; structurally invalid → its Zod
580
+ * issues as `E_VALIDATION`; in all three cases the tokens' bindings are left undecided.
581
+ * Otherwise the file's loader rows ({@link bindingsFileDiagnostics}).
582
+ */
583
+ function loadBindingsFrom(source) {
584
+ const raw = source.read(bindings_1.BINDINGS_FILE);
585
+ if (raw === null)
586
+ return { context: workflow_diagnostics_1.NO_BINDINGS_FILE, diagnostics: [] };
587
+ const undecided = { bindings: null, decided: false };
588
+ const yaml = parseYamlOrDiagnostic(raw, source.label(bindings_1.BINDINGS_FILE), bindings_1.BINDINGS_FILE);
589
+ if (yaml.diagnostic)
590
+ return { context: undecided, diagnostics: [yaml.diagnostic] };
591
+ // A bindings file from a newer WingFoil is refused for its format alone (`dl-153` (A), task-199).
592
+ const newerFormat = (0, format_1.newerFormatIssue)(yaml.data, format_1.BINDINGS_YAML_FORMAT, bindings_1.BINDINGS_FILE);
593
+ if (newerFormat)
594
+ return { context: undecided, diagnostics: [{ ...newerFormat, severity: 'error' }] };
595
+ const result = bindings_1.BindingsYaml.safeParse(yaml.data ?? {});
596
+ if (!result.success)
597
+ return { context: undecided, diagnostics: zodDiagnostics(result.error.issues, bindings_1.BINDINGS_FILE) };
598
+ (0, validation_1.emitUnknownFieldWarning)((yaml.data ?? {}), bindings_1.BindingsYaml, source.label(bindings_1.BINDINGS_FILE));
599
+ return { context: { bindings: result.data, decided: true }, diagnostics: (0, workflow_diagnostics_1.bindingsFileDiagnostics)(result.data) };
229
600
  }
230
601
  /**
231
602
  * Root-relative POSIX path of the Directives tree — the form git wants for a revision listing, as
@@ -241,8 +612,11 @@ exports.DIRECTIVES_DIR_PATH = '.wingfoil/directives';
241
612
  * cannot drift on what a directive file *is*.
242
613
  *
243
614
  * @param relativePath - The file's path relative to `.wingfoil/directives/`, POSIX-spelled.
615
+ * @param warn - Receives a `version` warning (task-144, R1). `src/core` prints nothing (task-143): the
616
+ * working-tree inventory forwards it to its `warnings`; a reader at a revision has no channel and
617
+ * drops it.
244
618
  */
245
- function parseDirectiveFile(raw, filePath, relativePath) {
619
+ function parseDirectiveFile(raw, filePath, relativePath, warn = () => { }) {
246
620
  const frontmatterText = (0, storage_1.extractFrontmatter)(raw);
247
621
  if (frontmatterText === null) {
248
622
  throw validation_1.ValidationError.semantic([
@@ -255,25 +629,79 @@ function parseDirectiveFile(raw, filePath, relativePath) {
255
629
  ]);
256
630
  }
257
631
  const data = (0, validation_1.parseYaml)(frontmatterText, filePath);
258
- const frontmatter = (0, validation_1.runValidation)(schema_1.DirectiveFrontmatter, data, filePath);
632
+ // A directive from a newer WingFoil is refused for its format alone (`dl-149`, task-251).
633
+ (0, format_1.refuseNewerFormat)(data, format_1.DIRECTIVE_FORMAT, filePath);
634
+ // `version` never fails the pillar (task-144 review, R1): a lossy number or a wrong type is a warning.
635
+ const versionCheck = (0, version_1.checkDirectiveVersion)(data, frontmatterText);
636
+ if (versionCheck.warning !== null)
637
+ warn(versionCheck.warning);
638
+ const frontmatter = (0, validation_1.runValidation)(schema_1.DirectiveFrontmatter, versionCheck.data, filePath);
259
639
  // `join`, so `DirectiveFile.path` carries the platform separator on both loaders — `requireCustomAsset`
260
640
  // and `selectDirectivesById` see one spelling whichever baseline produced the file.
261
641
  return { path: (0, path_1.join)('directives', ...relativePath.split('/')), frontmatter };
262
642
  }
263
643
  /**
264
- * Load and validate every Directives file in isolation (task-004-decoupled-pillars — see
265
- * `src/directives/schema.ts` for why this pillar has no dedicated approved tech-spec yet). Reads
644
+ * The working-tree directive inventory, warnings included: the form a caller with a channel of its
645
+ * own reads. `directives list` puts the warnings in its payload (`dl-042`), so they reach `--format
646
+ * json` readers and MCP clients; `directive remove` puts them in its refusal's `details`. `src/core`
647
+ * never prints them itself (task-143 review: a stray stderr line breaks spec-005 §3.2's one-object
648
+ * error under `--format json`).
649
+ *
650
+ * A `.md` file that cannot be **read** is skipped like any other unreadable entry: its bytes never
651
+ * reach the parser. A file that is read and then fails to parse or validate stays fatal — that is a
652
+ * broken directive, not a broken entry, and silently dropping a rule an agent must obey is worse than
653
+ * refusing the read. So is a `.wingfoil/directives` that cannot be listed: it is the pillar, and is
654
+ * refused as `E_DIRECTIVES_UNREADABLE`, naming it root-relative.
655
+ */
656
+ function loadDirectiveInventory(root) {
657
+ const directivesDir = (0, path_1.join)(root, '.wingfoil', 'directives');
658
+ const { entries, rootError } = listMarkdownFilesSorted(directivesDir);
659
+ if (rootError !== undefined) {
660
+ throw new validation_1.ValidationError([
661
+ { code: 'E_DIRECTIVES_UNREADABLE', path: '', file: '.wingfoil/directives', message: rootError },
662
+ ]);
663
+ }
664
+ const files = [];
665
+ const warnings = [];
666
+ const skip = (relative, reason) => {
667
+ const shown = relative === '' ? '.wingfoil/directives' : `.wingfoil/directives/${relative}`;
668
+ warnings.push(`directive entry '${shown}' skipped: ${reason}`);
669
+ };
670
+ for (const entry of entries) {
671
+ if (entry.kind === 'skipped') {
672
+ skip(entry.relative, entry.reason);
673
+ continue;
674
+ }
675
+ const absolute = (0, path_1.join)(directivesDir, entry.relative);
676
+ let raw;
677
+ try {
678
+ raw = (0, storage_1.readDocument)(absolute);
679
+ }
680
+ catch (error) {
681
+ skip(entry.relative, cannotBeRead(error));
682
+ continue;
683
+ }
684
+ files.push(parseDirectiveFile(raw, `.wingfoil/directives/${entry.relative}`, entry.relative, (warning) => warnings.push(`directive '.wingfoil/directives/${entry.relative}': ${warning}`)));
685
+ }
686
+ return { files, warnings };
687
+ }
688
+ /**
689
+ * Load and validate every Directives file in isolation (task-004-decoupled-pillars), against
690
+ * `spec-013-directive-frontmatter-schema`'s `DirectiveFrontmatter` (`src/directives/schema.ts`). Reads
266
691
  * every `.md` file under `.wingfoil/directives/**` (built-in + custom) **in the working tree**,
267
692
  * sorted deterministically (REQ-SYS-07: no unordered iteration in a context-building path), extracts
268
693
  * its frontmatter (`storage.extractFrontmatter`), and validates it against `DirectiveFrontmatter`.
269
694
  *
270
- * This is the *report* baseline — what the user has now — and it is what `directives list`, context
271
- * assembly and the MCP Resources read. A read that **gates** a mutation reads
272
- * {@link loadDirectivesAtHead} instead (`dl-080` (B)).
695
+ * This is the *report* baseline — what the user has now — and it is what context assembly and the
696
+ * MCP role Prompts read. A read that **gates** a mutation reads {@link loadDirectivesAtHead} instead
697
+ * (`dl-080` (B)).
698
+ *
699
+ * The files only: an entry that cannot be resolved or read is skipped (task-143, `bug-125`) and its
700
+ * warning dropped, since `src/core` does not print. A caller that can report the warnings reads
701
+ * {@link loadDirectiveInventory} instead.
273
702
  */
274
703
  function loadDirectives(root) {
275
- const directivesDir = (0, path_1.join)(root, '.wingfoil', 'directives');
276
- return listMarkdownFilesSorted(directivesDir).map((relativePath) => parseDirectiveFile((0, storage_1.readDocument)((0, path_1.join)(directivesDir, relativePath)), (0, path_1.join)(directivesDir, relativePath), relativePath));
704
+ return loadDirectiveInventory(root).files;
277
705
  }
278
706
  /**
279
707
  * Load and validate every Directives file **as the repository has committed it** — the tree at
@@ -301,19 +729,46 @@ function loadDirectives(root) {
301
729
  * unknown). `listPathsAtRev` keeps the distinction for callers that do need it.
302
730
  */
303
731
  function loadDirectivesAtHead(root) {
304
- const paths = (0, storage_1.listPathsAtRev)(root, 'HEAD', exports.DIRECTIVES_DIR_PATH) ?? [];
732
+ return (0, revision_1.atHeadOr)(root, () => loadDirectivesAtRev(root, 'HEAD'), []);
733
+ }
734
+ /**
735
+ * Load and validate every Directives file **as commit `rev` holds it** (task-137, `spec-012` §2
736
+ * `stateRef`) — the general form of {@link loadDirectivesAtHead}, which is this at `'HEAD'`: the
737
+ * `.md` files `storage.listPathsAtRev` lists under `.wingfoil/directives/` at that commit, in its
738
+ * sorted order, all read in one batch with `storage.readPathsAtRev` at the resolved sha (task-283). An empty array when the commit holds no
739
+ * directive file; a later commit and the working tree are never read.
740
+ *
741
+ * @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
742
+ * @throws `RevisionError` when `rev` is malformed or names no commit — never an empty array.
743
+ */
744
+ function loadDirectivesAtRev(root, rev) {
745
+ return directivesAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
746
+ }
747
+ /**
748
+ * {@link loadDirectivesAtRev} for a `sha` already resolved to a commit, read without resolving it again
749
+ * (task-283, `bug-309`: `workflow next`, `status` and `show` read the directives at their snapshot's commit).
750
+ *
751
+ * @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
752
+ */
753
+ function loadDirectivesAtCommit(root, sha) {
754
+ return directivesAt(root, sha, sha);
755
+ }
756
+ function directivesAt(root, sha, label) {
757
+ const paths = (0, revision_1.listPathsAtCommit)(root, sha, exports.DIRECTIVES_DIR_PATH).filter((path) => path.endsWith('.md'));
758
+ // One batched read (`readPathsAtRev`, one `git cat-file --batch`) for every directive, not one `git show`
759
+ // each (task-283, `bug-309`: 13 spawns per `workflow next` on this repository). `task-096`'s suite pins
760
+ // this read by name, through the `../storage` barrel.
761
+ const raws = (0, storage_1.readPathsAtRev)(root, sha, paths);
305
762
  const files = [];
306
- for (const path of paths) {
307
- if (!path.endsWith('.md'))
308
- continue;
309
- const raw = (0, storage_1.readPathAtRev)(root, 'HEAD', path);
310
- // Unreachable by construction — git has just listed this blob. Reachable only if the ref moved
311
- // between the two calls, and skipping is the fail-closed answer there: a directive nobody can read
312
- // is a directive that does not exist, so a binding to it is refused rather than committed.
763
+ paths.forEach((path, index) => {
764
+ const raw = raws[index];
765
+ // Unreachable by construction — git has just listed this blob at the same sha. Skipping is still
766
+ // the fail-closed answer: a directive nobody can read is a directive that does not exist, so a
767
+ // binding to it is refused rather than committed.
313
768
  if (raw === null)
314
- continue;
315
- files.push(parseDirectiveFile(raw, `HEAD:${path}`, path.slice(exports.DIRECTIVES_DIR_PATH.length + 1)));
316
- }
769
+ return;
770
+ files.push(parseDirectiveFile(raw, `${label}:${path}`, path.slice(exports.DIRECTIVES_DIR_PATH.length + 1)));
771
+ });
317
772
  return files;
318
773
  }
319
774
  /**
@@ -328,15 +783,16 @@ exports.ROLES_YAML_PATH = '.wingfoil/roles.yaml';
328
783
  * Load and validate `.wingfoil/roles.yaml` in isolation (task-037-role-task-scoped-context,
329
784
  * REQ-STATE-05's `directive-loader`, P3.2/P3.7 role → directive bindings) — the same shared two-pass
330
785
  * pipeline (`readDocument` + `parseYaml` + `runValidation`) every other pillar loader uses, so this
331
- * pillar's own validation never depends on another pillar's schema (REQ-SYS-02). Consumed by
332
- * `resolveRoleDirectives`/`assembleExecutionContext` (`./context.ts`) to resolve a role's assigned
333
- * directives.
786
+ * pillar's own validation never depends on another pillar's schema (REQ-SYS-02). The working-tree
787
+ * reader: `directives list` and the MCP role Prompts resolve a role's directives from it, while the
788
+ * execution context (`assembleExecutionContext`, `./context.ts`) reads {@link loadRolesYamlAtRev} at its
789
+ * `stateRef`.
334
790
  */
335
791
  function loadRolesYaml(root) {
336
- const filePath = (0, path_1.join)(root, '.wingfoil', 'roles.yaml');
337
- const raw = (0, storage_1.readDocument)(filePath);
338
- const data = (0, validation_1.parseYaml)(raw, filePath);
339
- return (0, validation_1.runValidation)(schema_1.RolesYaml, data, filePath);
792
+ const raw = readConfigFile(root, exports.ROLES_YAML_PATH);
793
+ const data = (0, validation_1.parseYaml)(raw, exports.ROLES_YAML_PATH);
794
+ (0, format_1.refuseNewerFormat)(data, format_1.ROLES_YAML_FORMAT, exports.ROLES_YAML_PATH);
795
+ return (0, validation_1.runValidation)(schema_1.RolesYaml, data, exports.ROLES_YAML_PATH);
340
796
  }
341
797
  /**
342
798
  * Load and validate `.wingfoil/roles.yaml` **as the repository has committed it** — the version at
@@ -355,10 +811,35 @@ function loadRolesYaml(root) {
355
811
  * committed `roles.yaml` that does not **validate** is the different case, and that one does refuse.
356
812
  */
357
813
  function loadRolesYamlAtHead(root) {
358
- const raw = (0, storage_1.readPathAtRev)(root, 'HEAD', exports.ROLES_YAML_PATH);
814
+ return (0, revision_1.atHeadOr)(root, () => loadRolesYamlAtRev(root, 'HEAD'), null);
815
+ }
816
+ /**
817
+ * Load and validate `.wingfoil/roles.yaml` **as commit `rev` holds it** (task-137, `spec-012` §2
818
+ * `stateRef`) — the general form of {@link loadRolesYamlAtHead}, which is this at `'HEAD'`. `null` when
819
+ * that commit does not hold the file; a later commit and the working tree are never read.
820
+ *
821
+ * @param rev - A revision naming one commit, resolved once (`resolveRevision`, `./revision.ts`).
822
+ * @throws `RevisionError` when `rev` is malformed or names no commit — never an empty answer.
823
+ */
824
+ function loadRolesYamlAtRev(root, rev) {
825
+ return rolesYamlAt(root, (0, revision_1.resolveRevision)(root, rev), rev);
826
+ }
827
+ /**
828
+ * {@link loadRolesYamlAtRev} for a `sha` already resolved to a commit, read without resolving it again
829
+ * (task-283, `bug-309`).
830
+ *
831
+ * @param sha - The full sha of a commit, as `resolveRevision` returned it; parse errors name it.
832
+ */
833
+ function loadRolesYamlAtCommit(root, sha) {
834
+ return rolesYamlAt(root, sha, sha);
835
+ }
836
+ function rolesYamlAt(root, sha, rev) {
837
+ const raw = (0, storage_1.readPathAtRev)(root, sha, exports.ROLES_YAML_PATH);
359
838
  if (raw === null)
360
839
  return null;
361
- const label = `HEAD:${exports.ROLES_YAML_PATH}`;
362
- return (0, validation_1.runValidation)(schema_1.RolesYaml, (0, validation_1.parseYaml)(raw, label), label);
840
+ const label = `${rev}:${exports.ROLES_YAML_PATH}`;
841
+ const data = (0, validation_1.parseYaml)(raw, label);
842
+ (0, format_1.refuseNewerFormat)(data, format_1.ROLES_YAML_FORMAT, label);
843
+ return (0, validation_1.runValidation)(schema_1.RolesYaml, data, label);
363
844
  }
364
845
  //# sourceMappingURL=loaders.js.map