@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.1

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 (1026) hide show
  1. package/CHANGELOG.md +2548 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +494 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +317 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +161 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +251 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +194 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +236 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +250 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +219 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +334 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +47 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +282 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +88 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  398. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  399. package/dist/core/c4/parsed/specification.js +78 -0
  400. package/dist/core/c4/parsed/specification.js.map +1 -0
  401. package/dist/core/c4/parsed/values.js +57 -0
  402. package/dist/core/c4/parsed/values.js.map +1 -0
  403. package/dist/core/c4/parsed/view-ids.js +75 -0
  404. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  405. package/dist/core/c4/project/architecture.js +45 -0
  406. package/dist/core/c4/project/architecture.js.map +1 -0
  407. package/dist/core/c4/project/documents.js +58 -0
  408. package/dist/core/c4/project/documents.js.map +1 -0
  409. package/dist/core/c4/project/load.js +183 -0
  410. package/dist/core/c4/project/load.js.map +1 -0
  411. package/dist/core/c4/resolve/service.js +113 -0
  412. package/dist/core/c4/resolve/service.js.map +1 -0
  413. package/dist/core/c4/seed/fleet-file.js +201 -0
  414. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  415. package/dist/core/c4/seed/items.js +91 -0
  416. package/dist/core/c4/seed/items.js.map +1 -0
  417. package/dist/core/c4/seed/stamp.js +48 -0
  418. package/dist/core/c4/seed/stamp.js.map +1 -0
  419. package/dist/core/c4/seed/template.js +109 -0
  420. package/dist/core/c4/seed/template.js.map +1 -0
  421. package/dist/core/c4/source-mask.js +82 -0
  422. package/dist/core/c4/source-mask.js.map +1 -0
  423. package/dist/core/c4/source-scan.js +227 -0
  424. package/dist/core/c4/source-scan.js.map +1 -0
  425. package/dist/core/c4/splice/authored-source.js +101 -0
  426. package/dist/core/c4/splice/authored-source.js.map +1 -0
  427. package/dist/core/c4/splice/contract.js +10 -0
  428. package/dist/core/c4/splice/contract.js.map +1 -0
  429. package/dist/core/c4/splice/delta-blocks.js +126 -0
  430. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  431. package/dist/core/c4/splice/identity/edges.js +36 -0
  432. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  433. package/dist/core/c4/splice/landscape-merge.js +269 -0
  434. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  435. package/dist/core/c4/splice/placement.js +194 -0
  436. package/dist/core/c4/splice/placement.js.map +1 -0
  437. package/dist/core/c4/workspace.js +172 -0
  438. package/dist/core/c4/workspace.js.map +1 -0
  439. package/dist/core/capabilities/authoring/notes.js +141 -0
  440. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  441. package/dist/core/capabilities/capabilities.js +124 -0
  442. package/dist/core/capabilities/capabilities.js.map +1 -0
  443. package/dist/core/capabilities/delta/doc.js +50 -0
  444. package/dist/core/capabilities/delta/doc.js.map +1 -0
  445. package/dist/core/capabilities/delta/overlay.js +129 -0
  446. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  447. package/dist/core/capabilities/delta/summary.js +57 -0
  448. package/dist/core/capabilities/delta/summary.js.map +1 -0
  449. package/dist/core/capabilities/delta/tree.js +43 -0
  450. package/dist/core/capabilities/delta/tree.js.map +1 -0
  451. package/dist/core/capabilities/delta/uncovered.js +222 -0
  452. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  453. package/dist/core/capabilities/findings.js +342 -0
  454. package/dist/core/capabilities/findings.js.map +1 -0
  455. package/dist/core/capabilities/realizes/findings.js +181 -0
  456. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  457. package/dist/core/capabilities/realizes/join.js +143 -0
  458. package/dist/core/capabilities/realizes/join.js.map +1 -0
  459. package/dist/core/capabilities/rollup.js +199 -0
  460. package/dist/core/capabilities/rollup.js.map +1 -0
  461. package/dist/core/capabilities/tree.js +126 -0
  462. package/dist/core/capabilities/tree.js.map +1 -0
  463. package/dist/core/capabilities/usecase-join.js +277 -0
  464. package/dist/core/capabilities/usecase-join.js.map +1 -0
  465. package/dist/core/coherence/authoring/scaffold.js +177 -0
  466. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  467. package/dist/core/coherence/authoring/sentinels.js +74 -0
  468. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  469. package/dist/core/coherence/coherence.js +251 -0
  470. package/dist/core/coherence/coherence.js.map +1 -0
  471. package/dist/core/coherence/declared.js +269 -0
  472. package/dist/core/coherence/declared.js.map +1 -0
  473. package/dist/core/coherence/events/declared.js +206 -0
  474. package/dist/core/coherence/events/declared.js.map +1 -0
  475. package/dist/core/coherence/events/events.js +204 -0
  476. package/dist/core/coherence/events/events.js.map +1 -0
  477. package/dist/core/coherence/events/grades.js +166 -0
  478. package/dist/core/coherence/events/grades.js.map +1 -0
  479. package/dist/core/coherence/events/lookups.js +122 -0
  480. package/dist/core/coherence/events/lookups.js.map +1 -0
  481. package/dist/core/coherence/events/merged.js +40 -0
  482. package/dist/core/coherence/events/merged.js.map +1 -0
  483. package/dist/core/coherence/living.js +198 -0
  484. package/dist/core/coherence/living.js.map +1 -0
  485. package/dist/core/coherence/lookups.js +155 -0
  486. package/dist/core/coherence/lookups.js.map +1 -0
  487. package/dist/core/coherence/pending.js +39 -0
  488. package/dist/core/coherence/pending.js.map +1 -0
  489. package/dist/core/conflict-markers.js +60 -0
  490. package/dist/core/conflict-markers.js.map +1 -0
  491. package/dist/core/delta/claims.js +73 -0
  492. package/dist/core/delta/claims.js.map +1 -0
  493. package/dist/core/delta/delta.js +296 -0
  494. package/dist/core/delta/delta.js.map +1 -0
  495. package/dist/core/delta/document.js +177 -0
  496. package/dist/core/delta/document.js.map +1 -0
  497. package/dist/core/delta/scope.js +48 -0
  498. package/dist/core/delta/scope.js.map +1 -0
  499. package/dist/core/delta/select.js +237 -0
  500. package/dist/core/delta/select.js.map +1 -0
  501. package/dist/core/dependencies/dependencies.js +141 -0
  502. package/dist/core/dependencies/dependencies.js.map +1 -0
  503. package/dist/core/dependencies/facts.js +165 -0
  504. package/dist/core/dependencies/facts.js.map +1 -0
  505. package/dist/core/dependencies/fanin.js +88 -0
  506. package/dist/core/dependencies/fanin.js.map +1 -0
  507. package/dist/core/dependencies/graph.js +106 -0
  508. package/dist/core/dependencies/graph.js.map +1 -0
  509. package/dist/core/diff/base-git.js +148 -0
  510. package/dist/core/diff/base-git.js.map +1 -0
  511. package/dist/core/diff/base-state.js +209 -0
  512. package/dist/core/diff/base-state.js.map +1 -0
  513. package/dist/core/diff/current-state.js +96 -0
  514. package/dist/core/diff/current-state.js.map +1 -0
  515. package/dist/core/diff/scope/changed-paths.js +188 -0
  516. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  517. package/dist/core/diff/semantic.js +243 -0
  518. package/dist/core/diff/semantic.js.map +1 -0
  519. package/dist/core/diff/victims.js +247 -0
  520. package/dist/core/diff/victims.js.map +1 -0
  521. package/dist/core/docs.js +7 -64
  522. package/dist/core/docs.js.map +1 -1
  523. package/dist/core/doctor/agents.js +222 -0
  524. package/dist/core/doctor/agents.js.map +1 -0
  525. package/dist/core/doctor/config.js +135 -0
  526. package/dist/core/doctor/config.js.map +1 -0
  527. package/dist/core/doctor/doctor.js +275 -0
  528. package/dist/core/doctor/doctor.js.map +1 -0
  529. package/dist/core/doctor/report.js +13 -0
  530. package/dist/core/doctor/report.js.map +1 -0
  531. package/dist/core/doctor/residue.js +110 -0
  532. package/dist/core/doctor/residue.js.map +1 -0
  533. package/dist/core/document/apply.js +77 -0
  534. package/dist/core/document/apply.js.map +1 -0
  535. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  536. package/dist/core/document/frontmatter.js.map +1 -0
  537. package/dist/core/document/grammar/directives.js +143 -0
  538. package/dist/core/document/grammar/directives.js.map +1 -0
  539. package/dist/core/document/parse.js +263 -0
  540. package/dist/core/document/parse.js.map +1 -0
  541. package/dist/core/document/scenarios.js +221 -0
  542. package/dist/core/document/scenarios.js.map +1 -0
  543. package/dist/core/document/spec.js +241 -0
  544. package/dist/core/document/spec.js.map +1 -0
  545. package/dist/core/{config.js → envelope/config.js} +79 -18
  546. package/dist/core/envelope/config.js.map +1 -0
  547. package/dist/core/envelope/json.js +175 -0
  548. package/dist/core/envelope/json.js.map +1 -0
  549. package/dist/core/{version.js → envelope/version.js} +3 -3
  550. package/dist/core/envelope/version.js.map +1 -0
  551. package/dist/core/explain/families.js +175 -0
  552. package/dist/core/explain/families.js.map +1 -0
  553. package/dist/core/explain/fix-tables.js +178 -0
  554. package/dist/core/explain/fix-tables.js.map +1 -0
  555. package/dist/core/explain/lookup.js +181 -0
  556. package/dist/core/explain/lookup.js.map +1 -0
  557. package/dist/core/explain/refusals.js +49 -0
  558. package/dist/core/explain/refusals.js.map +1 -0
  559. package/dist/core/explain/terms.js +144 -0
  560. package/dist/core/explain/terms.js.map +1 -0
  561. package/dist/core/explore/describe.js +105 -0
  562. package/dist/core/explore/describe.js.map +1 -0
  563. package/dist/core/{explore.js → explore/explore.js} +50 -101
  564. package/dist/core/explore/explore.js.map +1 -0
  565. package/dist/core/fleet-context.js +120 -78
  566. package/dist/core/fleet-context.js.map +1 -1
  567. package/dist/core/gate/checks.js +201 -0
  568. package/dist/core/gate/checks.js.map +1 -0
  569. package/dist/core/gate/partners.js +205 -0
  570. package/dist/core/gate/partners.js.map +1 -0
  571. package/dist/core/gate/report.js +47 -0
  572. package/dist/core/gate/report.js.map +1 -0
  573. package/dist/core/gate/verification.js +113 -0
  574. package/dist/core/gate/verification.js.map +1 -0
  575. package/dist/core/gherkin/digest.js +60 -0
  576. package/dist/core/gherkin/digest.js.map +1 -0
  577. package/dist/core/gherkin/emit.js +177 -0
  578. package/dist/core/gherkin/emit.js.map +1 -0
  579. package/dist/core/gherkin/read.js +76 -0
  580. package/dist/core/gherkin/read.js.map +1 -0
  581. package/dist/core/gherkin/stale.js +234 -0
  582. package/dist/core/gherkin/stale.js.map +1 -0
  583. package/dist/core/gherkin/stamp.js +77 -0
  584. package/dist/core/gherkin/stamp.js.map +1 -0
  585. package/dist/core/gherkin/steps/catalogue.js +154 -0
  586. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  587. package/dist/core/gherkin/steps/inventory.js +98 -0
  588. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  589. package/dist/core/gherkin/steps/phrase.js +72 -0
  590. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  591. package/dist/core/glossary/delta.js +106 -0
  592. package/dist/core/glossary/delta.js.map +1 -0
  593. package/dist/core/glossary/tree.js +86 -0
  594. package/dist/core/glossary/tree.js.map +1 -0
  595. package/dist/core/kernel/concurrency.js.map +1 -0
  596. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  597. package/dist/core/kernel/document-bytes.js.map +1 -0
  598. package/dist/core/kernel/fences.js +42 -0
  599. package/dist/core/kernel/fences.js.map +1 -0
  600. package/dist/core/kernel/ids/capability.js +72 -0
  601. package/dist/core/kernel/ids/capability.js.map +1 -0
  602. package/dist/core/kernel/ids/dirs.js +33 -0
  603. package/dist/core/kernel/ids/dirs.js.map +1 -0
  604. package/dist/core/kernel/ids/feature.js +75 -0
  605. package/dist/core/kernel/ids/feature.js.map +1 -0
  606. package/dist/core/kernel/ids/service.js +156 -0
  607. package/dist/core/kernel/ids/service.js.map +1 -0
  608. package/dist/core/kernel/ids/subsystem.js +63 -0
  609. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  610. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  611. package/dist/core/kernel/path-safety.js.map +1 -0
  612. package/dist/core/kernel/records.js.map +1 -0
  613. package/dist/core/links/backlinks.js +68 -0
  614. package/dist/core/links/backlinks.js.map +1 -0
  615. package/dist/core/links/case.js +74 -0
  616. package/dist/core/links/case.js.map +1 -0
  617. package/dist/core/links/corpus.js +104 -0
  618. package/dist/core/links/corpus.js.map +1 -0
  619. package/dist/core/links/findings.js +110 -0
  620. package/dist/core/links/findings.js.map +1 -0
  621. package/dist/core/links/parse.js +236 -0
  622. package/dist/core/links/parse.js.map +1 -0
  623. package/dist/core/mcp/argv.js +111 -0
  624. package/dist/core/mcp/argv.js.map +1 -0
  625. package/dist/core/mcp/author-tools.js +72 -0
  626. package/dist/core/mcp/author-tools.js.map +1 -0
  627. package/dist/core/mcp/framing.js +46 -0
  628. package/dist/core/mcp/framing.js.map +1 -0
  629. package/dist/core/mcp/protocol.js +280 -0
  630. package/dist/core/mcp/protocol.js.map +1 -0
  631. package/dist/core/mcp/tools.js +314 -0
  632. package/dist/core/mcp/tools.js.map +1 -0
  633. package/dist/core/obligations/obligations.js +106 -0
  634. package/dist/core/obligations/obligations.js.map +1 -0
  635. package/dist/core/openapi/baseline/gate.js +166 -0
  636. package/dist/core/openapi/baseline/gate.js.map +1 -0
  637. package/dist/core/openapi/baseline/plan.js +112 -0
  638. package/dist/core/openapi/baseline/plan.js.map +1 -0
  639. package/dist/core/openapi/baseline/record.js +175 -0
  640. package/dist/core/openapi/baseline/record.js.map +1 -0
  641. package/dist/core/openapi/depth.js +122 -0
  642. package/dist/core/openapi/depth.js.map +1 -0
  643. package/dist/core/openapi/digest.js +189 -0
  644. package/dist/core/openapi/digest.js.map +1 -0
  645. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  646. package/dist/core/openapi/doc.js.map +1 -0
  647. package/dist/core/openapi/merge/components.js +234 -0
  648. package/dist/core/openapi/merge/components.js.map +1 -0
  649. package/dist/core/openapi/merge/error.js +26 -0
  650. package/dist/core/openapi/merge/error.js.map +1 -0
  651. package/dist/core/openapi/merge/markers.js +208 -0
  652. package/dist/core/openapi/merge/markers.js.map +1 -0
  653. package/dist/core/openapi/merge/merge.js +256 -0
  654. package/dist/core/openapi/merge/merge.js.map +1 -0
  655. package/dist/core/openapi/merge/pin.js +128 -0
  656. package/dist/core/openapi/merge/pin.js.map +1 -0
  657. package/dist/core/openspec/decide/capabilities.js +244 -0
  658. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  659. package/dist/core/openspec/decide/dispositions.js +63 -0
  660. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  661. package/dist/core/openspec/decide/renames.js +179 -0
  662. package/dist/core/openspec/decide/renames.js.map +1 -0
  663. package/dist/core/openspec/decide/verdict.js +84 -0
  664. package/dist/core/openspec/decide/verdict.js.map +1 -0
  665. package/dist/core/openspec/inventory.js +221 -0
  666. package/dist/core/openspec/inventory.js.map +1 -0
  667. package/dist/core/openspec/model/mapping.js +76 -0
  668. package/dist/core/openspec/model/mapping.js.map +1 -0
  669. package/dist/core/openspec/model/model.js +65 -0
  670. package/dist/core/openspec/model/model.js.map +1 -0
  671. package/dist/core/openspec/scan/changes.js +235 -0
  672. package/dist/core/openspec/scan/changes.js.map +1 -0
  673. package/dist/core/openspec/scan/config.js +200 -0
  674. package/dist/core/openspec/scan/config.js.map +1 -0
  675. package/dist/core/openspec/scan/shape.js +260 -0
  676. package/dist/core/openspec/scan/shape.js.map +1 -0
  677. package/dist/core/openspec/scan/walk.js +133 -0
  678. package/dist/core/openspec/scan/walk.js.map +1 -0
  679. package/dist/core/openspec/scan/workspace.js +71 -0
  680. package/dist/core/openspec/scan/workspace.js.map +1 -0
  681. package/dist/core/owners/codeowners.js +141 -0
  682. package/dist/core/owners/codeowners.js.map +1 -0
  683. package/dist/core/pack/features.js +120 -0
  684. package/dist/core/pack/features.js.map +1 -0
  685. package/dist/core/pack/joins.js +112 -0
  686. package/dist/core/pack/joins.js.map +1 -0
  687. package/dist/core/pack/living.js +118 -0
  688. package/dist/core/pack/living.js.map +1 -0
  689. package/dist/core/pack/pack.js +76 -0
  690. package/dist/core/pack/pack.js.map +1 -0
  691. package/dist/core/permissions/permissions.js +87 -0
  692. package/dist/core/permissions/permissions.js.map +1 -0
  693. package/dist/core/projection/api.js +84 -0
  694. package/dist/core/projection/api.js.map +1 -0
  695. package/dist/core/projection/arch-slice.js +63 -0
  696. package/dist/core/projection/arch-slice.js.map +1 -0
  697. package/dist/core/projection/events.js +34 -0
  698. package/dist/core/projection/events.js.map +1 -0
  699. package/dist/core/provenance/findings.js +208 -0
  700. package/dist/core/provenance/findings.js.map +1 -0
  701. package/dist/core/provenance/git.js +225 -0
  702. package/dist/core/provenance/git.js.map +1 -0
  703. package/dist/core/provenance/gitq/moves.js +182 -0
  704. package/dist/core/provenance/gitq/moves.js.map +1 -0
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  707. package/dist/core/provenance/sample/scope.js +238 -0
  708. package/dist/core/provenance/sample/scope.js.map +1 -0
  709. package/dist/core/provenance/sample/sections.js +61 -0
  710. package/dist/core/provenance/sample/sections.js.map +1 -0
  711. package/dist/core/provenance/sources.js +263 -0
  712. package/dist/core/provenance/sources.js.map +1 -0
  713. package/dist/core/provenance/stamp.js +232 -0
  714. package/dist/core/provenance/stamp.js.map +1 -0
  715. package/dist/core/provenance/walk.js +157 -0
  716. package/dist/core/provenance/walk.js.map +1 -0
  717. package/dist/core/repo/authored/paths.js +192 -0
  718. package/dist/core/repo/authored/paths.js.map +1 -0
  719. package/dist/core/repo/entries.js +126 -0
  720. package/dist/core/repo/entries.js.map +1 -0
  721. package/dist/core/repo/paths.js +236 -0
  722. package/dist/core/repo/paths.js.map +1 -0
  723. package/dist/core/repo/repo.js +248 -0
  724. package/dist/core/repo/repo.js.map +1 -0
  725. package/dist/core/repo/service-target.js +153 -0
  726. package/dist/core/repo/service-target.js.map +1 -0
  727. package/dist/core/repo/state.js +58 -0
  728. package/dist/core/repo/state.js.map +1 -0
  729. package/dist/core/repo/tree/find.js +52 -0
  730. package/dist/core/repo/tree/find.js.map +1 -0
  731. package/dist/core/repo/tree/fs.js +64 -0
  732. package/dist/core/repo/tree/fs.js.map +1 -0
  733. package/dist/core/repo/tree/marker.js +61 -0
  734. package/dist/core/repo/tree/marker.js.map +1 -0
  735. package/dist/core/repo/tree/views.js +172 -0
  736. package/dist/core/repo/tree/views.js.map +1 -0
  737. package/dist/core/repo/tree/walk.js +234 -0
  738. package/dist/core/repo/tree/walk.js.map +1 -0
  739. package/dist/core/results.js +99 -2
  740. package/dist/core/results.js.map +1 -1
  741. package/dist/core/scaffold/landscape.js +131 -0
  742. package/dist/core/scaffold/landscape.js.map +1 -0
  743. package/dist/core/scaffold/readme.js +84 -0
  744. package/dist/core/scaffold/readme.js.map +1 -0
  745. package/dist/core/staging/commit.js +241 -0
  746. package/dist/core/staging/commit.js.map +1 -0
  747. package/dist/core/staging/interrupted.js +96 -0
  748. package/dist/core/staging/interrupted.js.map +1 -0
  749. package/dist/core/staging/lock.js +206 -0
  750. package/dist/core/staging/lock.js.map +1 -0
  751. package/dist/core/staging/recovery/finding.js +35 -0
  752. package/dist/core/staging/recovery/finding.js.map +1 -0
  753. package/dist/core/staging/recovery/intent.js +98 -0
  754. package/dist/core/staging/recovery/intent.js.map +1 -0
  755. package/dist/core/staging/recovery/recover.js +126 -0
  756. package/dist/core/staging/recovery/recover.js.map +1 -0
  757. package/dist/core/staging/recovery/residue.js +62 -0
  758. package/dist/core/staging/recovery/residue.js.map +1 -0
  759. package/dist/core/staging/snapshot.js +202 -0
  760. package/dist/core/staging/snapshot.js.map +1 -0
  761. package/dist/core/staging/txn/forward.js +171 -0
  762. package/dist/core/staging/txn/forward.js.map +1 -0
  763. package/dist/core/staging/txn/journal.js +139 -0
  764. package/dist/core/staging/txn/journal.js.map +1 -0
  765. package/dist/core/staging/txn/transaction.js +75 -0
  766. package/dist/core/staging/txn/transaction.js.map +1 -0
  767. package/dist/core/staging/writes.js +113 -0
  768. package/dist/core/staging/writes.js.map +1 -0
  769. package/dist/core/status/actions/execution.js +49 -0
  770. package/dist/core/status/actions/execution.js.map +1 -0
  771. package/dist/core/status/contracts.js +102 -0
  772. package/dist/core/status/contracts.js.map +1 -0
  773. package/dist/core/status/feature/artifacts.js +171 -0
  774. package/dist/core/status/feature/artifacts.js.map +1 -0
  775. package/dist/core/status/feature/feature.js +207 -0
  776. package/dist/core/status/feature/feature.js.map +1 -0
  777. package/dist/core/status/feature/next.js +150 -0
  778. package/dist/core/status/feature/next.js.map +1 -0
  779. package/dist/core/status/feature/state.js +21 -0
  780. package/dist/core/status/feature/state.js.map +1 -0
  781. package/dist/core/status/fleet/fleet.js +187 -0
  782. package/dist/core/status/fleet/fleet.js.map +1 -0
  783. package/dist/core/status/fleet/next.js +207 -0
  784. package/dist/core/status/fleet/next.js.map +1 -0
  785. package/dist/core/status/interrupted.js +97 -0
  786. package/dist/core/status/interrupted.js.map +1 -0
  787. package/dist/core/status/report.js +31 -0
  788. package/dist/core/status/report.js.map +1 -0
  789. package/dist/core/status/scan.js +41 -0
  790. package/dist/core/status/scan.js.map +1 -0
  791. package/dist/core/status/verification.js +201 -0
  792. package/dist/core/status/verification.js.map +1 -0
  793. package/dist/core/usecases/capability.js +169 -0
  794. package/dist/core/usecases/capability.js.map +1 -0
  795. package/dist/core/usecases/delta/claims.js +116 -0
  796. package/dist/core/usecases/delta/claims.js.map +1 -0
  797. package/dist/core/usecases/delta/flows.js +103 -0
  798. package/dist/core/usecases/delta/flows.js.map +1 -0
  799. package/dist/core/usecases/delta/overlay.js +235 -0
  800. package/dist/core/usecases/delta/overlay.js.map +1 -0
  801. package/dist/core/usecases/fleet.js +137 -0
  802. package/dist/core/usecases/fleet.js.map +1 -0
  803. package/dist/core/usecases/operations.js +111 -0
  804. package/dist/core/usecases/operations.js.map +1 -0
  805. package/dist/core/usecases/place.js +44 -0
  806. package/dist/core/usecases/place.js.map +1 -0
  807. package/dist/core/usecases/touch.js +101 -0
  808. package/dist/core/usecases/touch.js.map +1 -0
  809. package/dist/core/verify/answers.js +173 -0
  810. package/dist/core/verify/answers.js.map +1 -0
  811. package/dist/core/verify/build.js +150 -0
  812. package/dist/core/verify/build.js.map +1 -0
  813. package/dist/core/verify/checklist.js +201 -0
  814. package/dist/core/verify/checklist.js.map +1 -0
  815. package/dist/core/verify/claims/identity.js +61 -0
  816. package/dist/core/verify/claims/identity.js.map +1 -0
  817. package/dist/core/verify/cross/diff.js +97 -0
  818. package/dist/core/verify/cross/diff.js.map +1 -0
  819. package/dist/core/verify/evidence/contract.js +169 -0
  820. package/dist/core/verify/evidence/contract.js.map +1 -0
  821. package/dist/core/verify/file.js +257 -0
  822. package/dist/core/verify/file.js.map +1 -0
  823. package/dist/core/verify/pins/lint.js +157 -0
  824. package/dist/core/verify/pins/lint.js.map +1 -0
  825. package/dist/core/verify/pins/pin.js +95 -0
  826. package/dist/core/verify/pins/pin.js.map +1 -0
  827. package/dist/core/verify/record.js +142 -0
  828. package/dist/core/verify/record.js.map +1 -0
  829. package/dist/core/verify/store/commit.js +89 -0
  830. package/dist/core/verify/store/commit.js.map +1 -0
  831. package/dist/core/verify/store/render.js +57 -0
  832. package/dist/core/verify/store/render.js.map +1 -0
  833. package/dist/core/vocabulary/health.js +91 -0
  834. package/dist/core/vocabulary/health.js.map +1 -0
  835. package/dist/core/vocabulary/issue.js +55 -0
  836. package/dist/core/vocabulary/issue.js.map +1 -0
  837. package/dist/core/vocabulary/maturity.js +145 -0
  838. package/dist/core/vocabulary/maturity.js.map +1 -0
  839. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  840. package/dist/core/vocabulary/report.js.map +1 -0
  841. package/dist/core/vocabulary/steps.js +266 -0
  842. package/dist/core/vocabulary/steps.js.map +1 -0
  843. package/dist/core/workspace/discover.js +239 -0
  844. package/dist/core/workspace/discover.js.map +1 -0
  845. package/dist/core/workspace/render.js +52 -0
  846. package/dist/core/workspace/render.js.map +1 -0
  847. package/examples/README.md +189 -0
  848. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  849. package/examples/docs/architecture/capabilities.yaml +31 -0
  850. package/examples/docs/architecture/landscape.likec4 +280 -0
  851. package/examples/docs/architecture/obligations.yaml +23 -0
  852. package/examples/docs/architecture/permissions.yaml +50 -0
  853. package/examples/docs/architecture/subsystems.likec4 +11 -0
  854. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  855. package/examples/docs/capabilities/checkout/spec.md +55 -0
  856. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  857. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  858. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  859. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  860. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  863. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  864. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  865. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  866. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  867. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  868. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  869. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  870. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  871. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  872. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  873. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  874. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  875. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  876. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  877. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  878. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  879. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  882. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  883. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  884. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  885. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  886. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  887. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  888. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  889. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  890. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  891. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  896. package/examples/docs/glossary/order.md +15 -0
  897. package/examples/docs/glossary/payments/authorization.md +14 -0
  898. package/examples/docs/likec4.config.json +9 -0
  899. package/examples/docs/loam.json +3 -0
  900. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  901. package/examples/docs/services/checkout-web/spec.md +91 -0
  902. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  903. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  904. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  905. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  906. package/examples/docs/services/order-service/arch.spec.md +127 -0
  907. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  908. package/examples/docs/services/order-service/health.yaml +57 -0
  909. package/examples/docs/services/order-service/model.likec4 +138 -0
  910. package/examples/docs/services/order-service/openapi.yaml +257 -0
  911. package/examples/docs/services/order-service/runbook.md +62 -0
  912. package/examples/docs/services/order-service/spec.md +134 -0
  913. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  914. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  915. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  916. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  917. package/examples/docs/services/payment-service/health.yaml +39 -0
  918. package/examples/docs/services/payment-service/model.likec4 +67 -0
  919. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  920. package/examples/docs/services/payment-service/runbook.md +37 -0
  921. package/examples/docs/services/payment-service/spec.md +88 -0
  922. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  923. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  924. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  925. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  926. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  927. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  928. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  929. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  930. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  931. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  932. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  933. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  934. package/examples/docs/services/platform/subsystem.yaml +2 -0
  935. package/examples/fleet.yaml +57 -0
  936. package/package.json +19 -6
  937. package/dist/commands/adopt.js +0 -240
  938. package/dist/commands/adopt.js.map +0 -1
  939. package/dist/commands/archive.js +0 -1313
  940. package/dist/commands/archive.js.map +0 -1
  941. package/dist/commands/delta.js +0 -388
  942. package/dist/commands/delta.js.map +0 -1
  943. package/dist/commands/docs-repo-gate.js.map +0 -1
  944. package/dist/commands/format.js +0 -17
  945. package/dist/commands/format.js.map +0 -1
  946. package/dist/commands/gherkin.js +0 -309
  947. package/dist/commands/gherkin.js.map +0 -1
  948. package/dist/commands/init.js.map +0 -1
  949. package/dist/commands/list.js +0 -295
  950. package/dist/commands/list.js.map +0 -1
  951. package/dist/commands/migrate-openspec.js +0 -950
  952. package/dist/commands/migrate-openspec.js.map +0 -1
  953. package/dist/commands/new.js +0 -413
  954. package/dist/commands/new.js.map +0 -1
  955. package/dist/commands/rebase.js +0 -356
  956. package/dist/commands/rebase.js.map +0 -1
  957. package/dist/commands/show.js.map +0 -1
  958. package/dist/commands/status.js +0 -262
  959. package/dist/commands/status.js.map +0 -1
  960. package/dist/commands/unarchive.js.map +0 -1
  961. package/dist/commands/validate.js +0 -1773
  962. package/dist/commands/validate.js.map +0 -1
  963. package/dist/commands/verify.js +0 -775
  964. package/dist/commands/verify.js.map +0 -1
  965. package/dist/commands/vouch.js +0 -421
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2173
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js.map +0 -1
  970. package/dist/core/arch.js +0 -130
  971. package/dist/core/arch.js.map +0 -1
  972. package/dist/core/asyncapi.js.map +0 -1
  973. package/dist/core/brief.js +0 -755
  974. package/dist/core/brief.js.map +0 -1
  975. package/dist/core/coherence.js +0 -577
  976. package/dist/core/coherence.js.map +0 -1
  977. package/dist/core/concurrency.js.map +0 -1
  978. package/dist/core/config.js.map +0 -1
  979. package/dist/core/delta.js +0 -476
  980. package/dist/core/delta.js.map +0 -1
  981. package/dist/core/dependencies.js +0 -356
  982. package/dist/core/dependencies.js.map +0 -1
  983. package/dist/core/doctor.js +0 -596
  984. package/dist/core/doctor.js.map +0 -1
  985. package/dist/core/document-bytes.js.map +0 -1
  986. package/dist/core/explore.js.map +0 -1
  987. package/dist/core/frontmatter.js.map +0 -1
  988. package/dist/core/gherkin.js +0 -463
  989. package/dist/core/gherkin.js.map +0 -1
  990. package/dist/core/health.js +0 -67
  991. package/dist/core/health.js.map +0 -1
  992. package/dist/core/ids.js +0 -122
  993. package/dist/core/ids.js.map +0 -1
  994. package/dist/core/issue.js +0 -9
  995. package/dist/core/issue.js.map +0 -1
  996. package/dist/core/json.js +0 -74
  997. package/dist/core/json.js.map +0 -1
  998. package/dist/core/likec4.js +0 -489
  999. package/dist/core/likec4.js.map +0 -1
  1000. package/dist/core/maturity.js +0 -66
  1001. package/dist/core/maturity.js.map +0 -1
  1002. package/dist/core/openapi-merge.js +0 -552
  1003. package/dist/core/openapi-merge.js.map +0 -1
  1004. package/dist/core/openapi.js.map +0 -1
  1005. package/dist/core/openspec-inventory.js +0 -1440
  1006. package/dist/core/openspec-inventory.js.map +0 -1
  1007. package/dist/core/path-safety.js.map +0 -1
  1008. package/dist/core/provenance.js +0 -787
  1009. package/dist/core/provenance.js.map +0 -1
  1010. package/dist/core/records.js.map +0 -1
  1011. package/dist/core/repo.js +0 -438
  1012. package/dist/core/repo.js.map +0 -1
  1013. package/dist/core/report.js.map +0 -1
  1014. package/dist/core/spec.js +0 -505
  1015. package/dist/core/spec.js.map +0 -1
  1016. package/dist/core/staging.js +0 -890
  1017. package/dist/core/staging.js.map +0 -1
  1018. package/dist/core/status.js +0 -1132
  1019. package/dist/core/status.js.map +0 -1
  1020. package/dist/core/steps.js +0 -72
  1021. package/dist/core/steps.js.map +0 -1
  1022. package/dist/core/verify.js +0 -807
  1023. package/dist/core/verify.js.map +0 -1
  1024. package/dist/core/version.js.map +0 -1
  1025. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1026. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The emission's commit window: lock the owned root, recover a predecessor,
3
+ * stage every write and delete, prove the tree still matches what reconcile
4
+ * graded, and land it through the journaled transaction.
5
+ *
6
+ * Split from gherkin.ts so the lock's extent is visible in the code shape,
7
+ * exactly as vouch's commitStamp is — and because this is the writer the
8
+ * roadmap named: a kill inside the old write/delete loop left a half-old,
9
+ * half-new suite with no marker, in the one repo (the service's own) where
10
+ * nothing else of loam's ever looked.
11
+ */
12
+ import { existsSync } from "node:fs";
13
+ import { mkdir } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { quietRmdir, rollbackStaged, stageWrites } from "../../core/staging/commit.js";
16
+ import { COMMIT_INTENT, InterruptedCommitError } from "../../core/staging/interrupted.js";
17
+ import { acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from "../../core/staging/lock.js";
18
+ import { recoverInterruptedCommit } from "../../core/staging/recovery/recover.js";
19
+ import { commitStaged } from "../../core/staging/txn/transaction.js";
20
+ import {} from "../../core/staging/writes.js";
21
+ import {} from "./reconcile.js";
22
+ /**
23
+ * Roll a predecessor's interrupted emission forward before ANYTHING reads the
24
+ * suite — reconcile grades the very bytes a half-commit corrupts. Cheap when
25
+ * there is nothing to do: no journal, no lock, no I/O beyond one existsSync.
26
+ */
27
+ export async function recoverEmissionRoot(root) {
28
+ if (!existsSync(join(root, COMMIT_INTENT)))
29
+ return { ok: true, recovered: null };
30
+ let release;
31
+ try {
32
+ release = await acquireDocsLockWaiting(root, LOCK_WAIT_MS);
33
+ }
34
+ catch (err) {
35
+ if (err instanceof DocsBusyError)
36
+ return { ok: false, code: "docs-busy", message: err.message };
37
+ throw err;
38
+ }
39
+ try {
40
+ return { ok: true, recovered: await recoverInterruptedCommit(root) };
41
+ }
42
+ catch (err) {
43
+ if (err instanceof InterruptedCommitError)
44
+ return { ok: false, code: "commit-interrupted", message: err.message };
45
+ throw err;
46
+ }
47
+ finally {
48
+ await release();
49
+ }
50
+ }
51
+ /**
52
+ * Stage, compare, journal, swap. The compare is against the bytes RECONCILE
53
+ * read: a `.feature` that changed (or appeared) between the grading and this
54
+ * commit is refused by name, never buried — the suite is code somebody may be
55
+ * editing, and "refused, not buried" is the no-clobber rule the roadmap
56
+ * spells for exactly this writer.
57
+ */
58
+ export async function commitEmission(spec, plan) {
59
+ const { root, service, scope } = spec;
60
+ // The lock file lives in the root, so the root must exist first. If the
61
+ // commit then fails having created it, the empty directory is removed
62
+ // again — an empty loam/ flips the repo into "opted in" and tells
63
+ // `loam validate` the whole living suite is missing.
64
+ const rootExisted = existsSync(root);
65
+ await mkdir(root, { recursive: true });
66
+ let release;
67
+ try {
68
+ release = await acquireDocsLockWaiting(root, LOCK_WAIT_MS);
69
+ }
70
+ catch (err) {
71
+ if (!rootExisted)
72
+ await quietRmdir(root);
73
+ if (err instanceof DocsBusyError)
74
+ return { ok: false, code: "docs-busy", message: err.message };
75
+ throw err;
76
+ }
77
+ try {
78
+ // A writer can have crashed between the command-start recovery and this
79
+ // window; the re-check is one existsSync when nothing happened.
80
+ let recovered;
81
+ try {
82
+ recovered = await recoverInterruptedCommit(root);
83
+ }
84
+ catch (err) {
85
+ if (err instanceof InterruptedCommitError)
86
+ return { ok: false, code: "commit-interrupted", message: err.message };
87
+ throw err;
88
+ }
89
+ const planned = [
90
+ // A "written" row was graded against an ABSENT file, so its create is
91
+ // exclusive: a file appearing after planning loses the link(2) race and
92
+ // the run refuses rather than overwriting it.
93
+ ...plan.writes.map((a) => a.action === "written" ? { path: a.path, content: a.content, exclusive: true } : { path: a.path, content: a.content }),
94
+ ...plan.orphans.map((o) => ({ path: o.path, content: null })),
95
+ ];
96
+ const staged = await stageWrites(planned);
97
+ // The bytes reconcile graded must be the bytes this commit replaces or
98
+ // deletes. `swapStaged` re-checks against ITS read just before each
99
+ // rename; this compare closes the other half of the window, from the
100
+ // grading to the staging.
101
+ const graded = new Map();
102
+ for (const a of plan.writes)
103
+ if (a.action === "replaced")
104
+ graded.set(a.path, a.raw);
105
+ for (const o of plan.orphans)
106
+ graded.set(o.path, o.raw);
107
+ const moved = staged
108
+ .filter((s) => {
109
+ const raw = graded.get(s.write.path);
110
+ return raw !== undefined && (s.before === null || !s.before.equals(raw));
111
+ })
112
+ .map((s) => s.write.path);
113
+ if (moved.length > 0) {
114
+ await rollbackStaged(staged);
115
+ return {
116
+ ok: false,
117
+ code: "merge-failed",
118
+ message: `the suite changed while this emission was being planned — ${moved.join(", ")} ` +
119
+ `no longer holds the bytes the plan was graded against. Nothing was written: re-run.`,
120
+ };
121
+ }
122
+ const committed = await commitStaged({
123
+ root,
124
+ command: "gherkin",
125
+ rerun: scope.mode === "feature" ? `loam gherkin ${scope.featureId}` : "loam gherkin",
126
+ target: service,
127
+ }, staged, "emitted");
128
+ if (!committed.ok) {
129
+ return committed.raced
130
+ ? {
131
+ ok: false,
132
+ code: "merge-failed",
133
+ message: "a .feature file appeared after planning and the emission refused to overwrite it — " +
134
+ `${committed.message}. Nothing else was written: re-run to grade the new file.`,
135
+ }
136
+ : { ok: false, code: committed.code, message: committed.message };
137
+ }
138
+ return { ok: true, recovered };
139
+ }
140
+ finally {
141
+ await release();
142
+ if (!rootExisted && !existsSync(join(root, COMMIT_INTENT)))
143
+ await quietRmdir(root);
144
+ }
145
+ }
146
+ //# sourceMappingURL=commit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/commands/gherkin/commit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The emission's commit window: lock the owned root, recover a predecessor,\n * stage every write and delete, prove the tree still matches what reconcile\n * graded, and land it through the journaled transaction.\n *\n * Split from gherkin.ts so the lock's extent is visible in the code shape,\n * exactly as vouch's commitStamp is — and because this is the writer the\n * roadmap named: a kill inside the old write/delete loop left a half-old,\n * half-new suite with no marker, in the one repo (the service's own) where\n * nothing else of loam's ever looked.\n */\nimport { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { quietRmdir, rollbackStaged, stageWrites } from \"../../core/staging/commit.js\";\nimport { COMMIT_INTENT, type CommitRecovery, InterruptedCommitError } from \"../../core/staging/interrupted.js\";\nimport { acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from \"../../core/staging/lock.js\";\nimport { recoverInterruptedCommit } from \"../../core/staging/recovery/recover.js\";\nimport { commitStaged } from \"../../core/staging/txn/transaction.js\";\nimport { type PlannedWrite } from \"../../core/staging/writes.js\";\nimport { type ActionRow, type Scope } from \"./reconcile.js\";\n\nexport type EmissionCommit =\n | { ok: true; recovered: CommitRecovery | null }\n | { ok: false; code: \"docs-busy\" | \"commit-interrupted\" | \"merge-failed\" | \"rollback-incomplete\"; message: string };\n\n/**\n * Roll a predecessor's interrupted emission forward before ANYTHING reads the\n * suite — reconcile grades the very bytes a half-commit corrupts. Cheap when\n * there is nothing to do: no journal, no lock, no I/O beyond one existsSync.\n */\nexport async function recoverEmissionRoot(root: string): Promise<EmissionCommit> {\n if (!existsSync(join(root, COMMIT_INTENT))) return { ok: true, recovered: null };\n let release: () => Promise<void>;\n try {\n release = await acquireDocsLockWaiting(root, LOCK_WAIT_MS);\n } catch (err) {\n if (err instanceof DocsBusyError) return { ok: false, code: \"docs-busy\", message: err.message };\n throw err;\n }\n try {\n return { ok: true, recovered: await recoverInterruptedCommit(root) };\n } catch (err) {\n if (err instanceof InterruptedCommitError) return { ok: false, code: \"commit-interrupted\", message: err.message };\n throw err;\n } finally {\n await release();\n }\n}\n\n/**\n * Stage, compare, journal, swap. The compare is against the bytes RECONCILE\n * read: a `.feature` that changed (or appeared) between the grading and this\n * commit is refused by name, never buried — the suite is code somebody may be\n * editing, and \"refused, not buried\" is the no-clobber rule the roadmap\n * spells for exactly this writer.\n */\nexport async function commitEmission(\n spec: { root: string; service: string; scope: Scope },\n plan: { writes: ActionRow[]; orphans: { path: string; raw: Buffer }[] },\n): Promise<EmissionCommit> {\n const { root, service, scope } = spec;\n // The lock file lives in the root, so the root must exist first. If the\n // commit then fails having created it, the empty directory is removed\n // again — an empty loam/ flips the repo into \"opted in\" and tells\n // `loam validate` the whole living suite is missing.\n const rootExisted = existsSync(root);\n await mkdir(root, { recursive: true });\n let release: () => Promise<void>;\n try {\n release = await acquireDocsLockWaiting(root, LOCK_WAIT_MS);\n } catch (err) {\n if (!rootExisted) await quietRmdir(root);\n if (err instanceof DocsBusyError) return { ok: false, code: \"docs-busy\", message: err.message };\n throw err;\n }\n try {\n // A writer can have crashed between the command-start recovery and this\n // window; the re-check is one existsSync when nothing happened.\n let recovered: CommitRecovery | null;\n try {\n recovered = await recoverInterruptedCommit(root);\n } catch (err) {\n if (err instanceof InterruptedCommitError) return { ok: false, code: \"commit-interrupted\", message: err.message };\n throw err;\n }\n\n const planned: PlannedWrite[] = [\n // A \"written\" row was graded against an ABSENT file, so its create is\n // exclusive: a file appearing after planning loses the link(2) race and\n // the run refuses rather than overwriting it.\n ...plan.writes.map((a): PlannedWrite =>\n a.action === \"written\" ? { path: a.path, content: a.content, exclusive: true } : { path: a.path, content: a.content },\n ),\n ...plan.orphans.map((o): PlannedWrite => ({ path: o.path, content: null })),\n ];\n const staged = await stageWrites(planned);\n\n // The bytes reconcile graded must be the bytes this commit replaces or\n // deletes. `swapStaged` re-checks against ITS read just before each\n // rename; this compare closes the other half of the window, from the\n // grading to the staging.\n const graded = new Map<string, Buffer>();\n for (const a of plan.writes) if (a.action === \"replaced\") graded.set(a.path, a.raw);\n for (const o of plan.orphans) graded.set(o.path, o.raw);\n const moved = staged\n .filter((s) => {\n const raw = graded.get(s.write.path);\n return raw !== undefined && (s.before === null || !s.before.equals(raw));\n })\n .map((s) => s.write.path);\n if (moved.length > 0) {\n await rollbackStaged(staged);\n return {\n ok: false,\n code: \"merge-failed\",\n message:\n `the suite changed while this emission was being planned — ${moved.join(\", \")} ` +\n `no longer holds the bytes the plan was graded against. Nothing was written: re-run.`,\n };\n }\n\n const committed = await commitStaged(\n {\n root,\n command: \"gherkin\",\n rerun: scope.mode === \"feature\" ? `loam gherkin ${scope.featureId}` : \"loam gherkin\",\n target: service,\n },\n staged,\n \"emitted\",\n );\n if (!committed.ok) {\n return committed.raced\n ? {\n ok: false,\n code: \"merge-failed\",\n message:\n \"a .feature file appeared after planning and the emission refused to overwrite it — \" +\n `${committed.message}. Nothing else was written: re-run to grade the new file.`,\n }\n : { ok: false, code: committed.code, message: committed.message };\n }\n return { ok: true, recovered };\n } finally {\n await release();\n if (!rootExisted && !existsSync(join(root, COMMIT_INTENT))) await quietRmdir(root);\n }\n}\n\n"]}
@@ -0,0 +1,246 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { relative } from "node:path";
4
+ import { loadConfig } from "../../core/envelope/config.js";
5
+ import { decodeDocument, NotUtf8DocumentError } from "../../core/kernel/document-bytes.js";
6
+ import { emitJson, emitJsonError, fail, NO_SERVICE_MESSAGE, reportNoConfig, } from "../../core/envelope/json.js";
7
+ import { featureSpecPaths, SPEC_AXES } from "../../core/repo/paths.js";
8
+ import { locateServicePaths } from "../../core/repo/service-target.js";
9
+ import { listFeatures, missingFeatureMessage, resolveFeature } from "../../core/repo/repo.js";
10
+ import { parseRequirements } from "../../core/document/parse.js";
11
+ import {} from "../../core/document/spec.js";
12
+ import { axisLabel, planEmission } from "../../core/gherkin/emit.js";
13
+ import { gherkinRoot } from "../../core/gherkin/stamp.js";
14
+ import { LOAM_VERSION } from "../../core/envelope/version.js";
15
+ import { UnsafePathError } from "../../core/kernel/path-safety.js";
16
+ import { commitEmission, recoverEmissionRoot } from "./commit.js";
17
+ import { reconcile } from "./reconcile.js";
18
+ import { render } from "./render.js";
19
+ import { sayRecovered } from "../policy/format.js";
20
+ export function registerGherkin(program) {
21
+ program
22
+ .command("gherkin")
23
+ .argument("[featureId]", "feature id — emit its ADDED/MODIFIED requirements; omit for the full living suite")
24
+ .description("Emit Gherkin .feature files from spec scenarios into this service repo's <gherkinDir>/loam/")
25
+ .option("--service <id>", "service to emit for (defaults to the configured service)")
26
+ .option("--dry-run", "print the plan (writes, replacements, deletions) and write nothing — beyond first finishing a predecessor's interrupted commit, exactly as a real run would")
27
+ .option("--json", "emit the machine contract instead of the human view")
28
+ .action(async (featureArg, opts) => {
29
+ const json = opts.json === true;
30
+ const dryRun = opts.dryRun === true;
31
+ const loaded = await loadConfig();
32
+ if (loaded.kind !== "loaded") {
33
+ reportNoConfig(json, loaded);
34
+ return;
35
+ }
36
+ const config = loaded.config;
37
+ const service = opts.service ?? config.service;
38
+ if (service === undefined) {
39
+ return fail(json, "invalid-option", NO_SERVICE_MESSAGE);
40
+ }
41
+ // The emission writes into the repo loam is standing in, so it can only
42
+ // run where that repo is this service's — vouch's refusal, verbatim logic.
43
+ //
44
+ // TWO conditions, two codes, and the split is the whole point. Both used
45
+ // to answer `invalid-option`, whose `loam explain` row says the
46
+ // invocation itself is wrong — contradictory flags, a mistyped one, a
47
+ // value that cannot be right. Here the invocation is perfectly correct
48
+ // and the REPOSITORY is the wrong one, so that code sent a user whose
49
+ // flags were fine off to re-read their flags, and left CI and agents
50
+ // unable to tell "run this in the service repo" from "you typed the flag
51
+ // wrong". `loam verify --record --service` already draws exactly this
52
+ // line for exactly this reason (see commands/verify/verify.ts); gherkin
53
+ // and vouch now answer the same pair, so a caller branching on
54
+ // `error.code` gets one answer from all three. The messages are
55
+ // unchanged — only the code moved.
56
+ if (config.service === undefined) {
57
+ return fail(json, "repository-unavailable", `Cannot emit gherkin for '${service}' from here: this is not a service repo. Generated .feature files land in the service's own repo — run it there.`);
58
+ }
59
+ if (config.service !== service) {
60
+ return fail(json, "service-mismatch", `Cannot emit gherkin for '${service}' from here: this repo is '${config.service}'. Generated .feature files land in the service's own repo — run it there.`);
61
+ }
62
+ // The repo root, not the cwd: `config.root` is the directory loam.json
63
+ // was actually found in, and config discovery walks upward. Taken from
64
+ // `process.cwd()` this emitted the whole suite into whatever directory
65
+ // the caller happened to stand in — `src/api/features/loam/…` inside the
66
+ // source tree, which the next run from the root neither finds, replaces
67
+ // nor deletes, and which `loam validate` grades as a suite that was never
68
+ // generated.
69
+ const repoDir = config.root ?? process.cwd();
70
+ let root;
71
+ try {
72
+ root = gherkinRoot(repoDir, config.gherkinDir);
73
+ }
74
+ catch (err) {
75
+ if (!(err instanceof UnsafePathError))
76
+ throw err;
77
+ return fail(json, "invalid-option", `Cannot emit Gherkin: ${err.message}. The output directory must stay inside the service repo.`);
78
+ }
79
+ const rel = (abs) => relative(repoDir, abs).split(/[\\/]/).join("/");
80
+ // A predecessor's interrupted emission is rolled forward before ANY
81
+ // read — and before the feature argument resolves: reconcile grades the
82
+ // very bytes a half-commit corrupts, and the journal's stored rerun may
83
+ // name a feature that has since ARCHIVED, whose recovery must still
84
+ // work. `new` fixed the same ordering and documented it; refusing
85
+ // unknown-target over a wedged root left doctor printing a repair that
86
+ // could never run.
87
+ const early = await recoverEmissionRoot(root);
88
+ if (!early.ok)
89
+ return fail(json, early.code, early.message);
90
+ let recovered = early.recovered;
91
+ const feature = featureArg === undefined ? null : await resolveFeature(config.docsDir, featureArg, "exclude");
92
+ if (featureArg !== undefined && feature === null) {
93
+ return fail(json, "unknown-target", await missingFeatureMessage(config.docsDir, featureArg));
94
+ }
95
+ const scope = feature === null ? { mode: "living" } : { mode: "feature", featureId: feature.id };
96
+ // The requirement set, by scope. Feature mode takes ADDED and MODIFIED —
97
+ // a BASE requirement is the living state quoted, a REMOVED one is being
98
+ // retired, and neither is behaviour anybody is about to test. Living mode
99
+ // takes everything the living files hold.
100
+ //
101
+ // Read through `decodeDocument`, not `readFile(…, "utf8")`, and refused
102
+ // rather than degraded: a spec.md saved as UTF-16 decodes to a document
103
+ // with no requirements in it, which here is not merely a wrong answer but
104
+ // a destructive one — an empty plan makes every existing .feature under
105
+ // loam/ an orphan, and a living run DELETES its orphans. The whole
106
+ // generated suite would go because a docs file was written by PowerShell.
107
+ const byAxis = [];
108
+ const readSpec = async (path) => parseRequirements(decodeDocument(await readFile(path), path));
109
+ try {
110
+ if (feature !== null) {
111
+ // `config.service`, not `service`: the guard above proved they are
112
+ // the same id, and only the config's copy carries the load-time
113
+ // parse, so it is the pathable spelling — same as the else arm.
114
+ const paths = featureSpecPaths(feature.dir, config.service);
115
+ for (const axis of SPEC_AXES) {
116
+ const path = paths[axis.key];
117
+ const reqs = existsSync(path)
118
+ ? (await readSpec(path)).filter((r) => r.kind === "ADDED" || r.kind === "MODIFIED")
119
+ : [];
120
+ byAxis.push({ axis, reqs });
121
+ }
122
+ }
123
+ else {
124
+ // The guard above proved `service` IS `config.service`; only the
125
+ // latter carries the load-time parse, so it is the pathable spelling.
126
+ const paths = await locateServicePaths(config.docsDir, config.service);
127
+ if (!existsSync(paths.spec)) {
128
+ return fail(json, "unknown-target", `No living spec at ${paths.spec}. Run \`loam adopt\` for '${service}' first.`);
129
+ }
130
+ for (const axis of SPEC_AXES) {
131
+ const path = paths[axis.key];
132
+ const reqs = existsSync(path) ? (await readSpec(path)).filter((r) => r.kind !== "REMOVED") : [];
133
+ byAxis.push({ axis, reqs });
134
+ }
135
+ }
136
+ }
137
+ catch (err) {
138
+ if (!(err instanceof NotUtf8DocumentError))
139
+ throw err;
140
+ return fail(json, "repository-unavailable", `Cannot emit gherkin: ${err.message}`);
141
+ }
142
+ // `service` is not decoration: it salts every `@loam-digest-…` stamp, and
143
+ // it is the same string `loam verify` files the matching claims under.
144
+ const plan = planEmission(byAxis, {
145
+ service,
146
+ featureTag: scope.mode === "feature" ? scope.featureId : undefined,
147
+ version: LOAM_VERSION,
148
+ });
149
+ // Orphans of THIS scope, inside loam/ and nowhere else. Feature mode owns
150
+ // only its own emissions: stamped files carrying this feature's tag whose
151
+ // file is no longer in the plan (a requirement renamed or dropped).
152
+ // `Set<string>`, not the ids' own brand: this set answers membership
153
+ // questions for tags read out of .feature files — document text.
154
+ const activeIds = new Set((await listFeatures(config.docsDir)).map((f) => f.id));
155
+ let reconciled;
156
+ try {
157
+ reconciled = await reconcile(plan, { root, repoDir }, scope, activeIds);
158
+ }
159
+ catch (err) {
160
+ if (!(err instanceof UnsafePathError))
161
+ throw err;
162
+ return fail(json, "invalid-option", `Cannot emit Gherkin: ${err.message}.`);
163
+ }
164
+ const { actions, orphans, conflicts } = reconciled;
165
+ // All or nothing: one conflicting file refuses the whole emission, so a
166
+ // half-written suite can never be the state an agent has to reason about.
167
+ // Only a feature run can conflict — living mode keeps the in-flight file
168
+ // rather than refusing — and standing inside that scope is also how the
169
+ // message names the feature without asserting there is one.
170
+ if (scope.mode === "feature" && conflicts.length > 0) {
171
+ const detail = conflicts
172
+ .map((c) => `${rel(c.path)} is @${c.owners.join(" @")} (requirement '${c.requirement.name}')`)
173
+ .join("; ");
174
+ const message = `Cannot emit gherkin for ${scope.featureId}: ${conflicts.length} file(s) belong to another feature still in flight — ${detail}. ` +
175
+ `A .feature file carries one feature's delta; overwriting would revert that feature's wording and destroy the digest stamps \`loam verify --results\` matches on. ` +
176
+ `Archive (or abandon) the owning feature first, or rename the requirement in ${scope.featureId}'s delta so the two stop sharing a file name.`;
177
+ if (json) {
178
+ emitJsonError("gherkin-conflict", message, {
179
+ conflicts: conflicts.map((c) => ({
180
+ path: rel(c.path),
181
+ action: "conflict",
182
+ requirement: c.requirement.name,
183
+ inFlight: c.owners,
184
+ })),
185
+ });
186
+ return;
187
+ }
188
+ return fail(json, "gherkin-conflict", message);
189
+ }
190
+ const writes = actions.filter((a) => a.action !== "kept");
191
+ if (!dryRun && (writes.length > 0 || orphans.length > 0)) {
192
+ // The commit window: locked, journaled, compared against the bytes
193
+ // reconcile graded. A zero-op run takes no lock and creates no root —
194
+ // an emission with nothing to emit must not flip the repo into
195
+ // "opted in", where an empty loam/ tells `loam validate` the whole
196
+ // living suite is missing.
197
+ const committed = await commitEmission({ root, service, scope }, { writes, orphans });
198
+ if (!committed.ok)
199
+ return fail(json, committed.code, committed.message);
200
+ recovered = committed.recovered ?? recovered;
201
+ }
202
+ if (json) {
203
+ emitJson({
204
+ command: "gherkin",
205
+ mode: scope.mode,
206
+ ...(scope.mode === "feature" ? { feature: scope.featureId } : {}),
207
+ service,
208
+ root: rel(root),
209
+ written: !dryRun,
210
+ files: actions.map((a) => ({
211
+ path: rel(a.path),
212
+ action: a.action,
213
+ axis: axisLabel(a.axis),
214
+ requirement: a.requirement.name,
215
+ // A kept file's numbers describe what STAYS on disk (the in-flight
216
+ // emission), not the emission that was withheld.
217
+ scenarios: a.action === "kept" ? a.kept.scenarios.length : a.digests.length,
218
+ digests: a.action === "kept" ? a.kept.scenarios.map((s) => s.digest) : a.digests,
219
+ ...(a.action === "kept"
220
+ ? { inFlight: a.kept.tags.filter((t) => activeIds.has(t)) }
221
+ : {
222
+ stepless: a.stepless,
223
+ malformedExamples: a.malformedExamples,
224
+ strandedBlocks: a.strandedBlocks,
225
+ }),
226
+ })),
227
+ deleted: orphans.map((o) => rel(o.path)),
228
+ ...(recovered === null ? {} : { recovered }),
229
+ });
230
+ return;
231
+ }
232
+ if (recovered !== null)
233
+ console.log(`${sayRecovered(recovered)}\n`);
234
+ render(actions, {
235
+ service,
236
+ root: rel(root),
237
+ absRoot: root,
238
+ featureId: scope.mode === "feature" ? scope.featureId : null,
239
+ dryRun,
240
+ activeIds,
241
+ writes: writes.length,
242
+ orphans: orphans.map((o) => o.path),
243
+ });
244
+ });
245
+ }
246
+ //# sourceMappingURL=gherkin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gherkin.js","sourceRoot":"","sources":["../../../src/commands/gherkin/gherkin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam gherkin` — the third writer in the CLI (after archive and vouch), and\n * the only one that writes into the SERVICE repo, never the docs repo: the\n * docs state what must be true, the tests live with the code that makes it so.\n *\n * Two scopes, one emitter. `loam gherkin <FEAT>` emits the feature's ADDED and\n * MODIFIED requirements for this service — both spec axes — as the acceptance\n * skeleton to implement against, each file tagged with the feature id.\n * `loam gherkin --service <id>` (no feature) emits the full living suite from\n * spec.md + arch.spec.md: the regression skeleton a legacy service gets at\n * adoption. Either way the output is `<gherkinDir>/loam/` inside this repo,\n * a directory loam owns outright: the scope's files are rewritten, the scope's\n * orphans are deleted (and reported), and nothing outside `loam/` is touched —\n * except that a living run neither deletes NOR overwrites a file tagged with a\n * feature still in flight: it answers to that feature's delta until it\n * archives, and is reported as kept. A FEATURE run cannot keep — the file it\n * wants holds one feature's delta — so when the file it would write belongs to\n * a different feature still in flight, the whole run refuses (`gherkin-conflict`)\n * and names the owner rather than reverting it.\n *\n * It refuses to run anywhere but the service's own repo — vouch's discipline,\n * for vouch's reason: the output lands in the repo loam is standing in, and\n * from anywhere else that repo is somebody else's.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { decodeDocument, NotUtf8DocumentError } from \"../../core/kernel/document-bytes.js\";\nimport {\n emitJson,\n emitJsonError,\n fail,\n NO_SERVICE_MESSAGE,\n reportNoConfig,\n} from \"../../core/envelope/json.js\";\nimport { featureSpecPaths, SPEC_AXES, type SpecAxis } from \"../../core/repo/paths.js\";\nimport { locateServicePaths } from \"../../core/repo/service-target.js\";\nimport { listFeatures, missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport { parseRequirements } from \"../../core/document/parse.js\";\nimport { type Requirement } from \"../../core/document/spec.js\";\nimport { axisLabel, planEmission } from \"../../core/gherkin/emit.js\";\nimport { gherkinRoot } from \"../../core/gherkin/stamp.js\";\nimport { LOAM_VERSION } from \"../../core/envelope/version.js\";\nimport { UnsafePathError } from \"../../core/kernel/path-safety.js\";\nimport { commitEmission, recoverEmissionRoot } from \"./commit.js\";\nimport { reconcile, type Scope } from \"./reconcile.js\";\nimport { render } from \"./render.js\";\nimport { sayRecovered } from \"../policy/format.js\";\n\ninterface GherkinOptions {\n service?: string;\n dryRun?: boolean;\n json?: boolean;\n}\n\nexport function registerGherkin(program: Command): void {\n program\n .command(\"gherkin\")\n .argument(\"[featureId]\", \"feature id — emit its ADDED/MODIFIED requirements; omit for the full living suite\")\n .description(\n \"Emit Gherkin .feature files from spec scenarios into this service repo's <gherkinDir>/loam/\",\n )\n .option(\"--service <id>\", \"service to emit for (defaults to the configured service)\")\n .option(\n \"--dry-run\",\n \"print the plan (writes, replacements, deletions) and write nothing — beyond first finishing a predecessor's interrupted commit, exactly as a real run would\",\n )\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureArg: string | undefined, opts: GherkinOptions) => {\n const json = opts.json === true;\n const dryRun = opts.dryRun === true;\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const service = opts.service ?? config.service;\n if (service === undefined) {\n return fail(json, \"invalid-option\", NO_SERVICE_MESSAGE);\n }\n // The emission writes into the repo loam is standing in, so it can only\n // run where that repo is this service's — vouch's refusal, verbatim logic.\n //\n // TWO conditions, two codes, and the split is the whole point. Both used\n // to answer `invalid-option`, whose `loam explain` row says the\n // invocation itself is wrong — contradictory flags, a mistyped one, a\n // value that cannot be right. Here the invocation is perfectly correct\n // and the REPOSITORY is the wrong one, so that code sent a user whose\n // flags were fine off to re-read their flags, and left CI and agents\n // unable to tell \"run this in the service repo\" from \"you typed the flag\n // wrong\". `loam verify --record --service` already draws exactly this\n // line for exactly this reason (see commands/verify/verify.ts); gherkin\n // and vouch now answer the same pair, so a caller branching on\n // `error.code` gets one answer from all three. The messages are\n // unchanged — only the code moved.\n if (config.service === undefined) {\n return fail(\n json,\n \"repository-unavailable\",\n `Cannot emit gherkin for '${service}' from here: this is not a service repo. Generated .feature files land in the service's own repo — run it there.`,\n );\n }\n if (config.service !== service) {\n return fail(\n json,\n \"service-mismatch\",\n `Cannot emit gherkin for '${service}' from here: this repo is '${config.service}'. Generated .feature files land in the service's own repo — run it there.`,\n );\n }\n\n // The repo root, not the cwd: `config.root` is the directory loam.json\n // was actually found in, and config discovery walks upward. Taken from\n // `process.cwd()` this emitted the whole suite into whatever directory\n // the caller happened to stand in — `src/api/features/loam/…` inside the\n // source tree, which the next run from the root neither finds, replaces\n // nor deletes, and which `loam validate` grades as a suite that was never\n // generated.\n const repoDir = config.root ?? process.cwd();\n let root: string;\n try {\n root = gherkinRoot(repoDir, config.gherkinDir);\n } catch (err) {\n if (!(err instanceof UnsafePathError)) throw err;\n return fail(\n json,\n \"invalid-option\",\n `Cannot emit Gherkin: ${err.message}. The output directory must stay inside the service repo.`,\n );\n }\n const rel = (abs: string): string => relative(repoDir, abs).split(/[\\\\/]/).join(\"/\");\n\n // A predecessor's interrupted emission is rolled forward before ANY\n // read — and before the feature argument resolves: reconcile grades the\n // very bytes a half-commit corrupts, and the journal's stored rerun may\n // name a feature that has since ARCHIVED, whose recovery must still\n // work. `new` fixed the same ordering and documented it; refusing\n // unknown-target over a wedged root left doctor printing a repair that\n // could never run.\n const early = await recoverEmissionRoot(root);\n if (!early.ok) return fail(json, early.code, early.message);\n let recovered = early.recovered;\n\n const feature =\n featureArg === undefined ? null : await resolveFeature(config.docsDir, featureArg, \"exclude\");\n if (featureArg !== undefined && feature === null) {\n return fail(json, \"unknown-target\", await missingFeatureMessage(config.docsDir, featureArg));\n }\n const scope: Scope =\n feature === null ? { mode: \"living\" } : { mode: \"feature\", featureId: feature.id };\n\n // The requirement set, by scope. Feature mode takes ADDED and MODIFIED —\n // a BASE requirement is the living state quoted, a REMOVED one is being\n // retired, and neither is behaviour anybody is about to test. Living mode\n // takes everything the living files hold.\n //\n // Read through `decodeDocument`, not `readFile(…, \"utf8\")`, and refused\n // rather than degraded: a spec.md saved as UTF-16 decodes to a document\n // with no requirements in it, which here is not merely a wrong answer but\n // a destructive one — an empty plan makes every existing .feature under\n // loam/ an orphan, and a living run DELETES its orphans. The whole\n // generated suite would go because a docs file was written by PowerShell.\n const byAxis: Array<{ axis: SpecAxis; reqs: Requirement[] }> = [];\n const readSpec = async (path: string): Promise<Requirement[]> =>\n parseRequirements(decodeDocument(await readFile(path), path));\n try {\n if (feature !== null) {\n // `config.service`, not `service`: the guard above proved they are\n // the same id, and only the config's copy carries the load-time\n // parse, so it is the pathable spelling — same as the else arm.\n const paths = featureSpecPaths(feature.dir, config.service);\n for (const axis of SPEC_AXES) {\n const path = paths[axis.key];\n const reqs = existsSync(path)\n ? (await readSpec(path)).filter((r) => r.kind === \"ADDED\" || r.kind === \"MODIFIED\")\n : [];\n byAxis.push({ axis, reqs });\n }\n } else {\n // The guard above proved `service` IS `config.service`; only the\n // latter carries the load-time parse, so it is the pathable spelling.\n const paths = await locateServicePaths(config.docsDir, config.service);\n if (!existsSync(paths.spec)) {\n return fail(\n json,\n \"unknown-target\",\n `No living spec at ${paths.spec}. Run \\`loam adopt\\` for '${service}' first.`,\n );\n }\n for (const axis of SPEC_AXES) {\n const path = paths[axis.key];\n const reqs = existsSync(path) ? (await readSpec(path)).filter((r) => r.kind !== \"REMOVED\") : [];\n byAxis.push({ axis, reqs });\n }\n }\n } catch (err) {\n if (!(err instanceof NotUtf8DocumentError)) throw err;\n return fail(json, \"repository-unavailable\", `Cannot emit gherkin: ${err.message}`);\n }\n\n // `service` is not decoration: it salts every `@loam-digest-…` stamp, and\n // it is the same string `loam verify` files the matching claims under.\n const plan = planEmission(byAxis, {\n service,\n featureTag: scope.mode === \"feature\" ? scope.featureId : undefined,\n version: LOAM_VERSION,\n });\n\n // Orphans of THIS scope, inside loam/ and nowhere else. Feature mode owns\n // only its own emissions: stamped files carrying this feature's tag whose\n // file is no longer in the plan (a requirement renamed or dropped).\n\n // `Set<string>`, not the ids' own brand: this set answers membership\n // questions for tags read out of .feature files — document text.\n const activeIds = new Set<string>((await listFeatures(config.docsDir)).map((f) => f.id));\n let reconciled;\n try {\n reconciled = await reconcile(plan, { root, repoDir }, scope, activeIds);\n } catch (err) {\n if (!(err instanceof UnsafePathError)) throw err;\n return fail(json, \"invalid-option\", `Cannot emit Gherkin: ${err.message}.`);\n }\n const { actions, orphans, conflicts } = reconciled;\n\n // All or nothing: one conflicting file refuses the whole emission, so a\n // half-written suite can never be the state an agent has to reason about.\n // Only a feature run can conflict — living mode keeps the in-flight file\n // rather than refusing — and standing inside that scope is also how the\n // message names the feature without asserting there is one.\n if (scope.mode === \"feature\" && conflicts.length > 0) {\n const detail = conflicts\n .map((c) => `${rel(c.path)} is @${c.owners.join(\" @\")} (requirement '${c.requirement.name}')`)\n .join(\"; \");\n const message =\n `Cannot emit gherkin for ${scope.featureId}: ${conflicts.length} file(s) belong to another feature still in flight — ${detail}. ` +\n `A .feature file carries one feature's delta; overwriting would revert that feature's wording and destroy the digest stamps \\`loam verify --results\\` matches on. ` +\n `Archive (or abandon) the owning feature first, or rename the requirement in ${scope.featureId}'s delta so the two stop sharing a file name.`;\n if (json) {\n emitJsonError(\"gherkin-conflict\", message, {\n conflicts: conflicts.map((c) => ({\n path: rel(c.path),\n action: \"conflict\",\n requirement: c.requirement.name,\n inFlight: c.owners,\n })),\n });\n return;\n }\n return fail(json, \"gherkin-conflict\", message);\n }\n\n const writes = actions.filter((a) => a.action !== \"kept\");\n\n if (!dryRun && (writes.length > 0 || orphans.length > 0)) {\n // The commit window: locked, journaled, compared against the bytes\n // reconcile graded. A zero-op run takes no lock and creates no root —\n // an emission with nothing to emit must not flip the repo into\n // \"opted in\", where an empty loam/ tells `loam validate` the whole\n // living suite is missing.\n const committed = await commitEmission({ root, service, scope }, { writes, orphans });\n if (!committed.ok) return fail(json, committed.code, committed.message);\n recovered = committed.recovered ?? recovered;\n }\n\n if (json) {\n emitJson({\n command: \"gherkin\",\n mode: scope.mode,\n ...(scope.mode === \"feature\" ? { feature: scope.featureId } : {}),\n service,\n root: rel(root),\n written: !dryRun,\n files: actions.map((a) => ({\n path: rel(a.path),\n action: a.action,\n axis: axisLabel(a.axis),\n requirement: a.requirement.name,\n // A kept file's numbers describe what STAYS on disk (the in-flight\n // emission), not the emission that was withheld.\n scenarios: a.action === \"kept\" ? a.kept.scenarios.length : a.digests.length,\n digests: a.action === \"kept\" ? a.kept.scenarios.map((s) => s.digest) : a.digests,\n ...(a.action === \"kept\"\n ? { inFlight: a.kept.tags.filter((t) => activeIds.has(t)) }\n : {\n stepless: a.stepless,\n malformedExamples: a.malformedExamples,\n strandedBlocks: a.strandedBlocks,\n }),\n })),\n deleted: orphans.map((o) => rel(o.path)),\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n\n if (recovered !== null) console.log(`${sayRecovered(recovered)}\\n`);\n render(actions, {\n service,\n root: rel(root),\n absRoot: root,\n featureId: scope.mode === \"feature\" ? scope.featureId : null,\n dryRun,\n activeIds,\n writes: writes.length,\n orphans: orphans.map((o) => o.path),\n });\n });\n}\n"]}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * The plan against what is already on disk: which files are orphans, which are
3
+ * written, replaced, kept, or owned by another feature.
4
+ *
5
+ * A module of its own because it is the only part of `loam gherkin` that
6
+ * DELETES, and the reasoning that makes deletion safe is all here: a candidate
7
+ * is an orphan only when its containing directory really resolves inside the
8
+ * owned root, and a root that does not resolve proves nothing about what lives
9
+ * beneath it, so nothing is collected. Leaving an orphan behind is
10
+ * self-repairing — `loam validate` grades it and the next run removes it — and a
11
+ * deletion taken on an unproven path is not.
12
+ *
13
+ * The in-flight exemption is here for the same reason: files are named by
14
+ * requirement slug in both modes, so a MODIFIED requirement's living emission
15
+ * always collides with the active feature's file, and replacing it reverted the
16
+ * delta's wording mid-flight with its digest stamps destroyed.
17
+ */
18
+ import { existsSync, realpathSync } from "node:fs";
19
+ import { readFile } from "node:fs/promises";
20
+ import { dirname, join, relative } from "node:path";
21
+ import {} from "../../core/gherkin/emit.js";
22
+ import { parseStampedFeature } from "../../core/gherkin/read.js";
23
+ import { featureFilesUnder } from "../../core/gherkin/stale.js";
24
+ import { isPathInside, resolveInside, UnsafePathError } from "../../core/kernel/path-safety.js";
25
+ export async function reconcile(plan, where, scope, activeIds) {
26
+ const { root, repoDir } = where;
27
+ // Living mode owns the whole suite — any .feature not in the plan goes —
28
+ // EXCEPT files tagged with a feature still in flight: those answer to
29
+ // their feature's delta until it archives, and `loam gherkin <FEAT>` is
30
+ // their regeneration.
31
+ const planned = new Set(plan.map((f) => f.fileName));
32
+ // "Inside loam/" has to hold after the links are resolved, not merely in
33
+ // the spelling of the path. `featureFilesUnder` FOLLOWS symlinks and
34
+ // recurses with the LINK's path, so a planted `<gherkinDir>/loam/sub ->
35
+ // /outside` hands back candidates that read as ours while `unlink`
36
+ // resolves them and destroys files that were never in this repo. Planned
37
+ // names are flat, so nothing nested is ever spared by `planned.has(...)`,
38
+ // and deletion is the one irreversible thing this command does: a
39
+ // candidate is an orphan only when its CONTAINING directory really
40
+ // resolves inside the owned root. The write path below refuses the same
41
+ // attack for the same reason.
42
+ //
43
+ // The candidate FILE is deliberately NOT resolved. A symlinked .feature
44
+ // sitting directly in loam/ is loam's to remove — unlinking it takes the
45
+ // link and leaves its target alone — so only the directory chain has to
46
+ // be ours.
47
+ const realOrNull = (p) => {
48
+ try {
49
+ return realpathSync(p);
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ };
55
+ // A root that does not resolve — absent, dangling, or vanished mid-run —
56
+ // proves nothing about what lives beneath it, so nothing is collected.
57
+ // Leaving an orphan behind is self-repairing (`loam validate` grades it
58
+ // `gherkin.orphaned` and the next run removes it); a deletion taken on an
59
+ // unproven path is not.
60
+ const rootReal = realOrNull(root);
61
+ const orphans = [];
62
+ if (rootReal !== null) {
63
+ for (const abs of await featureFilesUnder(root)) {
64
+ if (planned.has(relative(root, abs).split(/[\\/]/).join("/")))
65
+ continue;
66
+ const holder = realOrNull(dirname(abs));
67
+ if (holder === null || !isPathInside(rootReal, holder))
68
+ continue;
69
+ const raw = await readFile(abs);
70
+ const stamped = parseStampedFeature(raw.toString("utf8"));
71
+ if (scope.mode === "feature") {
72
+ if (stamped !== null && stamped.tags.includes(scope.featureId))
73
+ orphans.push({ path: abs, raw });
74
+ }
75
+ else {
76
+ if (stamped !== null && stamped.tags.some((t) => activeIds.has(t)))
77
+ continue;
78
+ orphans.push({ path: abs, raw });
79
+ }
80
+ }
81
+ }
82
+ // The in-flight exemption guards the OVERWRITE path too, not only the
83
+ // orphan scan above: files are named by requirement slug in both modes,
84
+ // so a MODIFIED requirement's living emission always collides with the
85
+ // active feature's file — and replacing it reverted the delta's wording
86
+ // mid-flight, feature tag and new digest stamps destroyed, invisibly
87
+ // (the reverted file grades current against the living spec). A planned
88
+ // path whose existing content is stamped and tagged with a feature still
89
+ // in flight is KEPT and reported as such; it answers to its feature's
90
+ // delta until the feature archives, and then living regeneration
91
+ // replaces it normally.
92
+ //
93
+ // FEATURE mode has the same collision and cannot solve it by keeping:
94
+ // two features in flight against one requirement slug want the same
95
+ // file, and whichever runs second used to `replace` — silently reverting
96
+ // the other feature's wording, feature tag and digest stamps, so its
97
+ // `verify --results` could never confirm a scenario again. Nothing loam
98
+ // can write is right here (the file holds ONE feature's delta), so the
99
+ // run refuses and names the owner: the two features have to be sequenced,
100
+ // or the requirement renamed.
101
+ const actions = [];
102
+ for (const f of plan) {
103
+ let path;
104
+ try {
105
+ // Check the final file as well as the owned root: a pre-planted
106
+ // `<slug>.feature` symlink must not turn writeFile into an overwrite
107
+ // outside the repository.
108
+ path = resolveInside(repoDir, relative(repoDir, join(root, f.fileName)), `gherkin file '${f.fileName}'`);
109
+ }
110
+ catch (err) {
111
+ if (!(err instanceof UnsafePathError))
112
+ throw err;
113
+ throw err;
114
+ }
115
+ if (!existsSync(path)) {
116
+ actions.push({ ...f, path, action: "written" });
117
+ continue;
118
+ }
119
+ const raw = await readFile(path);
120
+ const existing = parseStampedFeature(raw.toString("utf8"));
121
+ if (scope.mode === "living") {
122
+ if (existing !== null && existing.tags.some((t) => activeIds.has(t))) {
123
+ actions.push({ ...f, path, action: "kept", kept: existing });
124
+ continue;
125
+ }
126
+ }
127
+ else if (existing !== null) {
128
+ // An unstamped file is nobody's delta — it owns nothing, so it is
129
+ // replaced like any other, which is what the empty owner list used
130
+ // to say the long way round.
131
+ const owners = existing.tags.filter((t) => t !== scope.featureId && activeIds.has(t));
132
+ if (owners.length > 0) {
133
+ actions.push({ ...f, path, action: "conflict", kept: existing, owners });
134
+ continue;
135
+ }
136
+ }
137
+ actions.push({ ...f, path, action: "replaced", raw });
138
+ }
139
+ // All or nothing: one conflicting file refuses the whole emission, so a
140
+ // half-written suite can never be the state an agent has to reason about.
141
+ // Only a feature run can conflict — living mode keeps the in-flight file
142
+ // rather than refusing — and standing inside that scope is also how the
143
+ // message names the feature without asserting there is one.
144
+ const conflicts = scope.mode === "feature"
145
+ ? actions.filter((a) => a.action === "conflict")
146
+ : [];
147
+ return { orphans, actions, conflicts };
148
+ }
149
+ //# sourceMappingURL=reconcile.js.map