@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,189 @@
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, fail, NO_SERVICE_MESSAGE, reportNoConfig } from "../../core/envelope/json.js";
7
+ import { SPEC_AXES } from "../../core/repo/paths.js";
8
+ import { locateServicePaths, resolveServiceTarget } from "../../core/repo/service-target.js";
9
+ import { parseRequirements } from "../../core/document/parse.js";
10
+ import { axisLabel } from "../../core/gherkin/emit.js";
11
+ import { coveringPhrases, stepInventory, } from "../../core/gherkin/steps/inventory.js";
12
+ import { compareToCatalogue, readStepCatalogue, } from "../../core/gherkin/steps/catalogue.js";
13
+ export function registerSteps(program) {
14
+ program
15
+ .command("steps")
16
+ .description("Inventory the step phrases of a service's scenarios — how many step definitions its suite needs")
17
+ .option("--service <id>", "service to inventory (defaults to the configured service)")
18
+ .option("--duplicates", "list only the near-duplicate groups — phrases that differ by an article or a trailing clause")
19
+ .option("--json", "emit the machine contract instead of the human view")
20
+ .action(async (opts) => {
21
+ const json = opts.json === true;
22
+ const loaded = await loadConfig();
23
+ if (loaded.kind !== "loaded") {
24
+ reportNoConfig(json, loaded);
25
+ return;
26
+ }
27
+ const config = loaded.config;
28
+ const named = opts.service ?? config.service;
29
+ if (named === undefined)
30
+ return fail(json, "invalid-option", NO_SERVICE_MESSAGE);
31
+ // The enumeration's own id, not the string the caller typed: only one of
32
+ // them carries the fact that a `readdir` produced it, and it is what makes
33
+ // the name pathable. Same resolution `validate --service` performs, so the
34
+ // two commands cannot disagree about which directory a name means.
35
+ const resolved = await resolveServiceTarget(config.docsDir, named, "--service");
36
+ if (!resolved.ok)
37
+ return fail(json, "invalid-option", resolved.problem);
38
+ const service = resolved.id;
39
+ const paths = await locateServicePaths(config.docsDir, service);
40
+ if (!existsSync(paths.spec)) {
41
+ return fail(json, "unknown-target", `No living spec at ${paths.spec}. Run \`loam adopt\` for '${service}' first.`);
42
+ }
43
+ const axes = [];
44
+ try {
45
+ for (const axis of SPEC_AXES) {
46
+ const path = paths[axis.key];
47
+ // An absent arch.spec.md is not a finding anywhere else and is not one
48
+ // here: partial adoption is supported, and an empty axis contributes
49
+ // no phrases rather than an empty section nobody asked for.
50
+ const reqs = existsSync(path)
51
+ ? parseRequirements(decodeDocument(await readFile(path), path))
52
+ : [];
53
+ axes.push({ axis: axisLabel(axis), reqs });
54
+ }
55
+ }
56
+ catch (err) {
57
+ if (!(err instanceof NotUtf8DocumentError))
58
+ throw err;
59
+ return fail(json, "repository-unavailable", `Cannot inventory steps: ${err.message}`);
60
+ }
61
+ const inv = stepInventory(axes);
62
+ // The catalogue is what makes the report a DIFF rather than a
63
+ // recomputation: without it every run says the same thing about a suite
64
+ // whose glue somebody has already written. Absent is the ordinary state
65
+ // and costs one `existsSync` (`core/gherkin/steps/catalogue.ts`).
66
+ const catalogue = await readStepCatalogue(paths.steps);
67
+ const against = catalogue.present && catalogue.unreadable === undefined
68
+ ? compareToCatalogue(inv, catalogue)
69
+ : null;
70
+ // Computed ONCE, above the branch, and derived from the path
71
+ // `locateServicePaths` resolved rather than joined from the id. A filed
72
+ // service lives wherever the tree walk found it — `services/commerce/
73
+ // checkout/`, not `services/checkout/` — and `core/repo/service-target.ts`
74
+ // says so at length. Two outputs of one command spelling the path two
75
+ // ways is how the human view came to name a directory that does not
76
+ // exist, and here that would be worse than cosmetic: `steps.yaml` is a
77
+ // service artifact now, so an author who created the file where this told
78
+ // them to would mint a second, empty service in the enumeration.
79
+ const where = relative(config.docsDir, paths.steps).split(/[\\/]/).join("/");
80
+ if (json) {
81
+ emitJson({
82
+ command: "steps",
83
+ docsDir: config.docsDir,
84
+ service,
85
+ steps: inv.steps,
86
+ phrases: inv.phrases.length,
87
+ covering80: coveringPhrases(inv),
88
+ rows: inv.phrases.map((p) => ({
89
+ key: p.key,
90
+ count: p.count,
91
+ keywords: p.keywords,
92
+ uses: p.uses,
93
+ })),
94
+ nearDuplicates: inv.nearDuplicates,
95
+ // Additive, and one key rather than columns spread through `rows`: a
96
+ // consumer that has not adopted the catalogue reads exactly what it
97
+ // read before. `present: false` and `unreadable` are different
98
+ // answers and both are real — "nobody has catalogued anything" is not
99
+ // "loam could not look".
100
+ catalogue: {
101
+ present: catalogue.present,
102
+ path: where,
103
+ entries: catalogue.entries.length,
104
+ ...(catalogue.unreadable === undefined ? {} : { unreadable: catalogue.unreadable }),
105
+ ...(against === null ? {} : against),
106
+ },
107
+ });
108
+ return;
109
+ }
110
+ print(inv, service, opts.duplicates === true);
111
+ printCatalogue({ catalogue, against, where });
112
+ });
113
+ }
114
+ /**
115
+ * The human view leads with the two numbers a team plans from — how many steps
116
+ * are written, and how few definitions cover most of them — because the list
117
+ * underneath is long by construction and the headline is what decides whether
118
+ * anybody reads it.
119
+ */
120
+ function print(inv, service, duplicatesOnly) {
121
+ if (inv.steps === 0) {
122
+ console.log(`steps ${service} — the living specs hold no scenario steps yet.\n`);
123
+ return;
124
+ }
125
+ const covering = coveringPhrases(inv);
126
+ const share = Math.round((100 * inv.phrases.reduce((a, p, i) => (i < covering ? a + p.count : a), 0)) / inv.steps);
127
+ console.log(`steps ${service} — ${inv.steps} written step(s), ${inv.phrases.length} distinct phrase(s)`);
128
+ console.log(` ${covering} phrase(s) cover ${share}% of them — that is the size of the step-definition registry\n`);
129
+ if (!duplicatesOnly) {
130
+ for (const p of inv.phrases) {
131
+ console.log(` ${String(p.count).padStart(4)} ${p.key}`);
132
+ console.log(` ${p.keywords.join("/")} · ${[...new Set(p.uses.map((u) => u.requirement))].join(", ")}`);
133
+ }
134
+ console.log();
135
+ }
136
+ if (inv.nearDuplicates.length === 0) {
137
+ console.log(" no near-duplicate phrases.");
138
+ return;
139
+ }
140
+ console.log(` ${inv.nearDuplicates.length} near-duplicate group(s) — one definition was probably meant:`);
141
+ for (const g of inv.nearDuplicates) {
142
+ console.log(` ${g.family}`);
143
+ for (const k of g.keys)
144
+ console.log(` · ${k}`);
145
+ }
146
+ }
147
+ /**
148
+ * The catalogue columns, printed after the inventory rather than woven into
149
+ * them.
150
+ *
151
+ * Separate because the two answers have different lifetimes: the inventory is a
152
+ * fact about the specs and is always true, while this is a diff against a
153
+ * decision that may not have been made yet. Weaving a "not catalogued" marker
154
+ * through every phrase row of a service with no `steps.yaml` would mark the
155
+ * whole report as work owed, which is the opposite of what an absent catalogue
156
+ * means.
157
+ *
158
+ * Nothing here is a finding and nothing changes the exit code. `loam steps` is a
159
+ * read, and a phrase written before its glue is the normal order of work.
160
+ */
161
+ function printCatalogue({ catalogue, against, where }) {
162
+ if (!catalogue.present) {
163
+ console.log(`\n no ${where} — nothing records which phrases this suite has decided to define.`);
164
+ console.log(" Write one (a `steps:` list of step texts) and this report becomes a diff instead of a recount.");
165
+ return;
166
+ }
167
+ if (catalogue.unreadable !== undefined) {
168
+ console.log(`\n ${where} could not be read — ${catalogue.unreadable}.`);
169
+ console.log(" The catalogue columns are blank: loam did not look, which is not the same as nothing being catalogued.");
170
+ return;
171
+ }
172
+ if (against === null)
173
+ return;
174
+ console.log(`\n ${catalogue.entries.length} phrase(s) catalogued in ${where}`);
175
+ console.log(` ${against.uncatalogued.length} written but not catalogued · ${against.unwritten.length} catalogued but not written`);
176
+ for (const key of against.uncatalogued)
177
+ console.log(` + ${key}`);
178
+ for (const key of against.unwritten)
179
+ console.log(` - ${key}`);
180
+ if (against.duplicated.length === 0)
181
+ return;
182
+ console.log(` ${against.duplicated.length} catalogue entr(ies) collapse onto a phrase already listed:`);
183
+ for (const dup of against.duplicated) {
184
+ console.log(` ${dup.key}`);
185
+ for (const text of dup.texts)
186
+ console.log(` · ${text}`);
187
+ }
188
+ }
189
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../src/commands/steps/steps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam steps` — the step-phrase inventory of one service's living specs.\n *\n * A READ, and that is the whole design. It emits no finding, sets no exit code\n * beyond a refusal, writes nothing, and needs no service repo to stand in: it\n * parses `spec.md` and `arch.spec.md` from the docs repo exactly as `validate\n * --service` does. `loam gherkin` refuses outside the service's own repository\n * because it WRITES there; this answers a question about documents, so the\n * restriction would buy nothing and cost the analyst who wants the number\n * before anybody has scaffolded a repo.\n *\n * What it is for is in `core/gherkin/steps/inventory.ts`. In one line: the glue\n * layer is where a generated suite dies, and until this existed nobody could\n * tell a suite needing forty step definitions from one needing four hundred.\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 { emitJson, fail, NO_SERVICE_MESSAGE, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { SPEC_AXES } from \"../../core/repo/paths.js\";\nimport { locateServicePaths, resolveServiceTarget } from \"../../core/repo/service-target.js\";\nimport { parseRequirements } from \"../../core/document/parse.js\";\nimport { axisLabel } from \"../../core/gherkin/emit.js\";\nimport {\n coveringPhrases,\n stepInventory,\n type InventoryAxis,\n type StepInventory,\n} from \"../../core/gherkin/steps/inventory.js\";\nimport {\n compareToCatalogue,\n readStepCatalogue,\n type CatalogueComparison,\n type StepCatalogue,\n} from \"../../core/gherkin/steps/catalogue.js\";\n\ninterface StepsOptions {\n service?: string;\n duplicates?: boolean;\n json?: boolean;\n}\n\nexport function registerSteps(program: Command): void {\n program\n .command(\"steps\")\n .description(\"Inventory the step phrases of a service's scenarios — how many step definitions its suite needs\")\n .option(\"--service <id>\", \"service to inventory (defaults to the configured service)\")\n .option(\"--duplicates\", \"list only the near-duplicate groups — phrases that differ by an article or a trailing clause\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (opts: StepsOptions) => {\n const json = opts.json === true;\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const named = opts.service ?? config.service;\n if (named === undefined) return fail(json, \"invalid-option\", NO_SERVICE_MESSAGE);\n // The enumeration's own id, not the string the caller typed: only one of\n // them carries the fact that a `readdir` produced it, and it is what makes\n // the name pathable. Same resolution `validate --service` performs, so the\n // two commands cannot disagree about which directory a name means.\n const resolved = await resolveServiceTarget(config.docsDir, named, \"--service\");\n if (!resolved.ok) return fail(json, \"invalid-option\", resolved.problem);\n const service = resolved.id;\n\n const paths = await locateServicePaths(config.docsDir, 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 const axes: InventoryAxis[] = [];\n try {\n for (const axis of SPEC_AXES) {\n const path = paths[axis.key];\n // An absent arch.spec.md is not a finding anywhere else and is not one\n // here: partial adoption is supported, and an empty axis contributes\n // no phrases rather than an empty section nobody asked for.\n const reqs = existsSync(path)\n ? parseRequirements(decodeDocument(await readFile(path), path))\n : [];\n axes.push({ axis: axisLabel(axis), reqs });\n }\n } catch (err) {\n if (!(err instanceof NotUtf8DocumentError)) throw err;\n return fail(json, \"repository-unavailable\", `Cannot inventory steps: ${err.message}`);\n }\n\n const inv = stepInventory(axes);\n // The catalogue is what makes the report a DIFF rather than a\n // recomputation: without it every run says the same thing about a suite\n // whose glue somebody has already written. Absent is the ordinary state\n // and costs one `existsSync` (`core/gherkin/steps/catalogue.ts`).\n const catalogue = await readStepCatalogue(paths.steps);\n const against = catalogue.present && catalogue.unreadable === undefined\n ? compareToCatalogue(inv, catalogue)\n : null;\n // Computed ONCE, above the branch, and derived from the path\n // `locateServicePaths` resolved rather than joined from the id. A filed\n // service lives wherever the tree walk found it — `services/commerce/\n // checkout/`, not `services/checkout/` — and `core/repo/service-target.ts`\n // says so at length. Two outputs of one command spelling the path two\n // ways is how the human view came to name a directory that does not\n // exist, and here that would be worse than cosmetic: `steps.yaml` is a\n // service artifact now, so an author who created the file where this told\n // them to would mint a second, empty service in the enumeration.\n const where = relative(config.docsDir, paths.steps).split(/[\\\\/]/).join(\"/\");\n if (json) {\n emitJson({\n command: \"steps\",\n docsDir: config.docsDir,\n service,\n steps: inv.steps,\n phrases: inv.phrases.length,\n covering80: coveringPhrases(inv),\n rows: inv.phrases.map((p) => ({\n key: p.key,\n count: p.count,\n keywords: p.keywords,\n uses: p.uses,\n })),\n nearDuplicates: inv.nearDuplicates,\n // Additive, and one key rather than columns spread through `rows`: a\n // consumer that has not adopted the catalogue reads exactly what it\n // read before. `present: false` and `unreadable` are different\n // answers and both are real — \"nobody has catalogued anything\" is not\n // \"loam could not look\".\n catalogue: {\n present: catalogue.present,\n path: where,\n entries: catalogue.entries.length,\n ...(catalogue.unreadable === undefined ? {} : { unreadable: catalogue.unreadable }),\n ...(against === null ? {} : against),\n },\n });\n return;\n }\n print(inv, service, opts.duplicates === true);\n printCatalogue({ catalogue, against, where });\n });\n}\n\n/**\n * The human view leads with the two numbers a team plans from — how many steps\n * are written, and how few definitions cover most of them — because the list\n * underneath is long by construction and the headline is what decides whether\n * anybody reads it.\n */\nfunction print(inv: StepInventory, service: string, duplicatesOnly: boolean): void {\n if (inv.steps === 0) {\n console.log(`steps ${service} — the living specs hold no scenario steps yet.\\n`);\n return;\n }\n const covering = coveringPhrases(inv);\n const share = Math.round((100 * inv.phrases.reduce((a, p, i) => (i < covering ? a + p.count : a), 0)) / inv.steps);\n console.log(`steps ${service} — ${inv.steps} written step(s), ${inv.phrases.length} distinct phrase(s)`);\n console.log(` ${covering} phrase(s) cover ${share}% of them — that is the size of the step-definition registry\\n`);\n\n if (!duplicatesOnly) {\n for (const p of inv.phrases) {\n console.log(` ${String(p.count).padStart(4)} ${p.key}`);\n console.log(` ${p.keywords.join(\"/\")} · ${[...new Set(p.uses.map((u) => u.requirement))].join(\", \")}`);\n }\n console.log();\n }\n\n if (inv.nearDuplicates.length === 0) {\n console.log(\" no near-duplicate phrases.\");\n return;\n }\n console.log(` ${inv.nearDuplicates.length} near-duplicate group(s) — one definition was probably meant:`);\n for (const g of inv.nearDuplicates) {\n console.log(` ${g.family}`);\n for (const k of g.keys) console.log(` · ${k}`);\n }\n}\n\n/** What the catalogue columns print from — the read and, when it was worth making, the comparison. */\ninterface CatalogueView {\n catalogue: StepCatalogue;\n /** Null when there was nothing readable to compare against. */\n against: CatalogueComparison | null;\n /**\n * The catalogue's docs-relative path, RESOLVED — never `services/<id>/…`\n * rejoined from the service name, which is right for an unfiled service and\n * wrong for every filed one. The same string `--json` reports.\n */\n where: string;\n}\n\n/**\n * The catalogue columns, printed after the inventory rather than woven into\n * them.\n *\n * Separate because the two answers have different lifetimes: the inventory is a\n * fact about the specs and is always true, while this is a diff against a\n * decision that may not have been made yet. Weaving a \"not catalogued\" marker\n * through every phrase row of a service with no `steps.yaml` would mark the\n * whole report as work owed, which is the opposite of what an absent catalogue\n * means.\n *\n * Nothing here is a finding and nothing changes the exit code. `loam steps` is a\n * read, and a phrase written before its glue is the normal order of work.\n */\nfunction printCatalogue({ catalogue, against, where }: CatalogueView): void {\n if (!catalogue.present) {\n console.log(`\\n no ${where} — nothing records which phrases this suite has decided to define.`);\n console.log(\" Write one (a `steps:` list of step texts) and this report becomes a diff instead of a recount.\");\n return;\n }\n if (catalogue.unreadable !== undefined) {\n console.log(`\\n ${where} could not be read — ${catalogue.unreadable}.`);\n console.log(\" The catalogue columns are blank: loam did not look, which is not the same as nothing being catalogued.\");\n return;\n }\n if (against === null) return;\n console.log(`\\n ${catalogue.entries.length} phrase(s) catalogued in ${where}`);\n console.log(\n ` ${against.uncatalogued.length} written but not catalogued · ${against.unwritten.length} catalogued but not written`,\n );\n for (const key of against.uncatalogued) console.log(` + ${key}`);\n for (const key of against.unwritten) console.log(` - ${key}`);\n if (against.duplicated.length === 0) return;\n console.log(` ${against.duplicated.length} catalogue entr(ies) collapse onto a phrase already listed:`);\n for (const dup of against.duplicated) {\n console.log(` ${dup.key}`);\n for (const text of dup.texts) console.log(` · ${text}`);\n }\n}\n"]}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * The two verbs that create and remove GROUPS: `subsystem new` and
3
+ * `subsystem rm`. Both commit the marker AND the regenerated views file in
4
+ * one journaled transaction, because a marker without its view (or a view
5
+ * outliving its marker) is exactly the `subsystem.views-stale` state the
6
+ * generated file's contract refuses to leave behind.
7
+ *
8
+ * `rm` refuses a subsystem with members — services, child subsystems, or any
9
+ * stray non-dot entry beside the marker the tree does not already count —
10
+ * naming each once, as what it is (`subsystem-not-empty`):
11
+ * a destructive command never picks targets the caller did not name, and a
12
+ * directory emptied of its marker but not of its contents would re-enter the
13
+ * walk as a phantom service.
14
+ */
15
+ import { existsSync } from "node:fs";
16
+ import { readdir, rmdir } from "node:fs/promises";
17
+ import { join } from "node:path";
18
+ import { stringify } from "yaml";
19
+ import { parseSubsystemName } from "../../core/kernel/ids/subsystem.js";
20
+ import { emitJson, fail, repoPath } from "../../core/envelope/json.js";
21
+ import { servicesDir, subsystemPathUnder } from "../../core/repo/paths.js";
22
+ import { findInTree, nearestTreeNames, servicesUnder, subsystemsUnder } from "../../core/repo/tree/find.js";
23
+ import { entryIs } from "../../core/repo/tree/fs.js";
24
+ import { SUBSYSTEM_MARKER } from "../../core/repo/tree/marker.js";
25
+ import { planWrite } from "../../core/staging/writes.js";
26
+ import { commitWindow, reportViews } from "./txn/txn.js";
27
+ /** `subsystem new <name> [--under <parent>] [--title …] [--description …] [--owner …]` */
28
+ export async function runNew(docsDir, name, opts, json) {
29
+ const parsed = parseSubsystemName(name, "subsystem name");
30
+ if (!parsed.ok) {
31
+ fail(json, "invalid-option", parsed.problem);
32
+ return;
33
+ }
34
+ await commitWindow(docsDir, json, async (tree) => {
35
+ // The flat namespace: one name, one meaning, at any depth — a `new` that
36
+ // collides with ANY service or subsystem would be the exact state
37
+ // `subsystem.name-collision` refuses after the fact.
38
+ const taken = findInTree(tree, name);
39
+ if (taken !== null) {
40
+ const where = taken.kind === "service" ? taken.service.dir : taken.subsystem.dir;
41
+ fail(json, "already-exists", `'${name}' is already a ${taken.kind} (${repoPath(docsDir, where)}) — ` +
42
+ `service ids and subsystem names share one flat namespace, unique at any depth. Pick another name.`);
43
+ return null;
44
+ }
45
+ let parentDir = servicesDir(docsDir);
46
+ let parentPath = [];
47
+ if (opts.under !== undefined) {
48
+ const parent = findInTree(tree, opts.under);
49
+ if (parent === null) {
50
+ const close = nearestTreeNames(opts.under, tree.subsystems.map((s) => s.name));
51
+ fail(json, "unknown-target", `No subsystem '${opts.under}' in the tree.` +
52
+ (close.length > 0 ? ` Close names: ${close.join(", ")}.` : " `loam subsystem list` shows what exists."));
53
+ return null;
54
+ }
55
+ if (parent.kind === "service") {
56
+ fail(json, "invalid-option", `--under names the service '${opts.under}' — a service never contains subsystems.`);
57
+ return null;
58
+ }
59
+ parentDir = parent.subsystem.dir;
60
+ parentPath = parent.subsystem.path;
61
+ }
62
+ const dir = subsystemPathUnder(parentDir, parsed.name);
63
+ // The flat-namespace check above compares exact strings; the FILESYSTEM
64
+ // may not. On a case-folding volume (macOS APFS by default) `Billing`
65
+ // and an existing `services/billing` are one directory, so the marker
66
+ // write below would land subsystem.yaml INSIDE the live service — the
67
+ // exact marker-beside-artifacts state `subsystem.marker-misplaced`
68
+ // refuses after the fact, created by a blessed command. A plain file of
69
+ // the same name reaches here too (files at the root are not walked). So
70
+ // the disk is probed directly and anything already there refuses closed.
71
+ if (existsSync(dir)) {
72
+ fail(json, "already-exists", `${repoPath(docsDir, dir)} already exists on disk — the tree resolves no entry by that exact name, ` +
73
+ `so either this filesystem folds case (the directory answers to another spelling of the name) ` +
74
+ `or something non-loam created it. Pick another name, or move what is there first. Nothing was created.`);
75
+ return null;
76
+ }
77
+ const meta = {
78
+ ...(opts.title === undefined ? {} : { title: opts.title }),
79
+ ...(opts.description === undefined ? {} : { description: opts.description }),
80
+ ...(opts.owner === undefined ? {} : { owner: opts.owner }),
81
+ };
82
+ // An empty file is a valid marker — presence classifies, content is metadata.
83
+ const marker = Object.keys(meta).length === 0 ? "" : stringify(meta);
84
+ const after = {
85
+ ...tree,
86
+ subsystems: [...tree.subsystems, { name, path: [...parentPath, name], dir, meta }],
87
+ };
88
+ return {
89
+ target: name,
90
+ writes: [planWrite(join(dir, SUBSYSTEM_MARKER), marker)],
91
+ tree: after,
92
+ // A lost exclusive-create race on the marker IS the name being taken.
93
+ racedCode: "already-exists",
94
+ what: "created",
95
+ report: (views, recovered) => {
96
+ if (json) {
97
+ emitJson({
98
+ command: "subsystem",
99
+ created: name,
100
+ path: repoPath(docsDir, dir),
101
+ views,
102
+ ...(recovered === null ? {} : { recovered }),
103
+ });
104
+ return;
105
+ }
106
+ console.log(`created ${repoPath(docsDir, dir)}/ (${SUBSYSTEM_MARKER} written)${reportViews(views)}`);
107
+ },
108
+ };
109
+ });
110
+ }
111
+ /** `subsystem rm <name>` — refuses anything but an empty group. */
112
+ export async function runRm(docsDir, name, json) {
113
+ await commitWindow(docsDir, json, async (tree) => {
114
+ const hit = findInTree(tree, name);
115
+ if (hit === null) {
116
+ const close = nearestTreeNames(name, tree.subsystems.map((s) => s.name));
117
+ fail(json, "unknown-target", `No subsystem '${name}' in the tree.` +
118
+ (close.length > 0 ? ` Close names: ${close.join(", ")}.` : " `loam subsystem list` shows what exists."));
119
+ return null;
120
+ }
121
+ if (hit.kind === "service") {
122
+ fail(json, "invalid-option", `'${name}' is a service, not a subsystem — loam does not delete services (\`git rm -r ${repoPath(docsDir, hit.service.dir)}\` does, and version control keeps the record).`);
123
+ return null;
124
+ }
125
+ const sub = hit.subsystem;
126
+ const services = servicesUnder(tree, sub).map((s) => s.id);
127
+ const children = subsystemsUnder(tree, sub).map((s) => s.name);
128
+ // Stray entries beside the marker count as members too: after the marker
129
+ // is gone the directory would re-enter the walk as an empty "service" the
130
+ // moment anything keeps rmdir from removing it. But only entries the tree
131
+ // has NOT already counted — a member service or child subsystem is also a
132
+ // readdir entry here, and listing it a second time as "file X" made the
133
+ // count wrong and the same name appear under two kinds. What remains is
134
+ // discriminated by what it is (`entryIs`, the walk's own symlink-aware
135
+ // question): a file, or a directory the tree does not claim (an unmarked
136
+ // group, already its own `subsystem.unmarked` finding).
137
+ const counted = new Set([
138
+ ...servicesUnder(tree, sub).filter((s) => s.subsystem.length === sub.path.length).map((s) => s.id),
139
+ ...subsystemsUnder(tree, sub).filter((c) => c.path.length === sub.path.length + 1).map((c) => c.name),
140
+ ]);
141
+ const strays = (await readdir(sub.dir, { withFileTypes: true }))
142
+ .filter((e) => !e.name.startsWith(".") && e.name !== SUBSYSTEM_MARKER && !counted.has(e.name))
143
+ .map((e) => `${entryIs(sub.dir, e, "dir") ? "directory" : "file"} ${e.name}`);
144
+ const members = [
145
+ ...services.map((s) => `service ${s}`),
146
+ ...children.map((c) => `subsystem ${c}`),
147
+ ...strays,
148
+ ];
149
+ if (members.length > 0) {
150
+ fail(json, "subsystem-not-empty", `'${name}' still holds ${members.length} member(s): ${members.join(", ")}. ` +
151
+ "`loam subsystem move <name>... --into <sub|.>` moves them out; a destructive command never picks targets you did not name.");
152
+ return null;
153
+ }
154
+ const after = { ...tree, subsystems: tree.subsystems.filter((s) => s !== sub) };
155
+ return {
156
+ target: name,
157
+ writes: [{ path: join(sub.dir, SUBSYSTEM_MARKER), content: null }],
158
+ tree: after,
159
+ what: "removed",
160
+ report: async (views, recovered) => {
161
+ // The directory itself, after the transaction: the marker is gone, the
162
+ // group was verifiably empty, and an empty marker-less directory would
163
+ // read as an unfiled service. Dotfile litter (.DS_Store) can still
164
+ // defeat rmdir — reported, not swallowed.
165
+ let directoryRemoved = true;
166
+ try {
167
+ await rmdir(sub.dir);
168
+ }
169
+ catch {
170
+ directoryRemoved = !existsSync(sub.dir);
171
+ }
172
+ if (json) {
173
+ emitJson({
174
+ command: "subsystem",
175
+ removed: name,
176
+ path: repoPath(docsDir, sub.dir),
177
+ directoryRemoved,
178
+ views,
179
+ ...(recovered === null ? {} : { recovered }),
180
+ });
181
+ return;
182
+ }
183
+ console.log(`removed subsystem '${name}' (${repoPath(docsDir, sub.dir)}/)${reportViews(views)}`);
184
+ if (!directoryRemoved) {
185
+ console.log(`note: the directory itself could not be removed (something non-loam is still inside) — ` +
186
+ `until it is deleted by hand it will list as an empty unfiled service.`);
187
+ }
188
+ },
189
+ };
190
+ });
191
+ }
192
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/commands/subsystem/edit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The two verbs that create and remove GROUPS: `subsystem new` and\n * `subsystem rm`. Both commit the marker AND the regenerated views file in\n * one journaled transaction, because a marker without its view (or a view\n * outliving its marker) is exactly the `subsystem.views-stale` state the\n * generated file's contract refuses to leave behind.\n *\n * `rm` refuses a subsystem with members — services, child subsystems, or any\n * stray non-dot entry beside the marker the tree does not already count —\n * naming each once, as what it is (`subsystem-not-empty`):\n * a destructive command never picks targets the caller did not name, and a\n * directory emptied of its marker but not of its contents would re-enter the\n * walk as a phantom service.\n */\nimport { existsSync } from \"node:fs\";\nimport { readdir, rmdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { stringify } from \"yaml\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseSubsystemName } from \"../../core/kernel/ids/subsystem.js\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { servicesDir, subsystemPathUnder } from \"../../core/repo/paths.js\";\nimport { findInTree, nearestTreeNames, servicesUnder, subsystemsUnder } from \"../../core/repo/tree/find.js\";\nimport { entryIs } from \"../../core/repo/tree/fs.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { planWrite } from \"../../core/staging/writes.js\";\nimport { commitWindow, reportViews, type SubsystemTxn } from \"./txn/txn.js\";\nimport type { SubsystemOptions } from \"./subsystem.js\";\n\n/** `subsystem new <name> [--under <parent>] [--title …] [--description …] [--owner …]` */\nexport async function runNew(\n docsDir: DocsDir,\n name: string,\n opts: SubsystemOptions,\n json: boolean,\n): Promise<void> {\n const parsed = parseSubsystemName(name, \"subsystem name\");\n if (!parsed.ok) {\n fail(json, \"invalid-option\", parsed.problem);\n return;\n }\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n // The flat namespace: one name, one meaning, at any depth — a `new` that\n // collides with ANY service or subsystem would be the exact state\n // `subsystem.name-collision` refuses after the fact.\n const taken = findInTree(tree, name);\n if (taken !== null) {\n const where = taken.kind === \"service\" ? taken.service.dir : taken.subsystem.dir;\n fail(\n json,\n \"already-exists\",\n `'${name}' is already a ${taken.kind} (${repoPath(docsDir, where)}) — ` +\n `service ids and subsystem names share one flat namespace, unique at any depth. Pick another name.`,\n );\n return null;\n }\n let parentDir = servicesDir(docsDir);\n let parentPath: string[] = [];\n if (opts.under !== undefined) {\n const parent = findInTree(tree, opts.under);\n if (parent === null) {\n const close = nearestTreeNames(opts.under, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${opts.under}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n if (parent.kind === \"service\") {\n fail(json, \"invalid-option\", `--under names the service '${opts.under}' — a service never contains subsystems.`);\n return null;\n }\n parentDir = parent.subsystem.dir;\n parentPath = parent.subsystem.path;\n }\n const dir = subsystemPathUnder(parentDir, parsed.name);\n // The flat-namespace check above compares exact strings; the FILESYSTEM\n // may not. On a case-folding volume (macOS APFS by default) `Billing`\n // and an existing `services/billing` are one directory, so the marker\n // write below would land subsystem.yaml INSIDE the live service — the\n // exact marker-beside-artifacts state `subsystem.marker-misplaced`\n // refuses after the fact, created by a blessed command. A plain file of\n // the same name reaches here too (files at the root are not walked). So\n // the disk is probed directly and anything already there refuses closed.\n if (existsSync(dir)) {\n fail(\n json,\n \"already-exists\",\n `${repoPath(docsDir, dir)} already exists on disk — the tree resolves no entry by that exact name, ` +\n `so either this filesystem folds case (the directory answers to another spelling of the name) ` +\n `or something non-loam created it. Pick another name, or move what is there first. Nothing was created.`,\n );\n return null;\n }\n const meta = {\n ...(opts.title === undefined ? {} : { title: opts.title }),\n ...(opts.description === undefined ? {} : { description: opts.description }),\n ...(opts.owner === undefined ? {} : { owner: opts.owner }),\n };\n // An empty file is a valid marker — presence classifies, content is metadata.\n const marker = Object.keys(meta).length === 0 ? \"\" : stringify(meta);\n const after: FleetTree = {\n ...tree,\n subsystems: [...tree.subsystems, { name, path: [...parentPath, name], dir, meta }],\n };\n return {\n target: name,\n writes: [planWrite(join(dir, SUBSYSTEM_MARKER), marker)],\n tree: after,\n // A lost exclusive-create race on the marker IS the name being taken.\n racedCode: \"already-exists\",\n what: \"created\",\n report: (views, recovered) => {\n if (json) {\n emitJson({\n command: \"subsystem\",\n created: name,\n path: repoPath(docsDir, dir),\n views,\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`created ${repoPath(docsDir, dir)}/ (${SUBSYSTEM_MARKER} written)${reportViews(views)}`);\n },\n };\n });\n}\n\n/** `subsystem rm <name>` — refuses anything but an empty group. */\nexport async function runRm(docsDir: DocsDir, name: string, json: boolean): Promise<void> {\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const hit = findInTree(tree, name);\n if (hit === null) {\n const close = nearestTreeNames(name, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${name}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(\n json,\n \"invalid-option\",\n `'${name}' is a service, not a subsystem — loam does not delete services (\\`git rm -r ${repoPath(docsDir, hit.service.dir)}\\` does, and version control keeps the record).`,\n );\n return null;\n }\n const sub = hit.subsystem;\n const services = servicesUnder(tree, sub).map((s) => s.id);\n const children = subsystemsUnder(tree, sub).map((s) => s.name);\n // Stray entries beside the marker count as members too: after the marker\n // is gone the directory would re-enter the walk as an empty \"service\" the\n // moment anything keeps rmdir from removing it. But only entries the tree\n // has NOT already counted — a member service or child subsystem is also a\n // readdir entry here, and listing it a second time as \"file X\" made the\n // count wrong and the same name appear under two kinds. What remains is\n // discriminated by what it is (`entryIs`, the walk's own symlink-aware\n // question): a file, or a directory the tree does not claim (an unmarked\n // group, already its own `subsystem.unmarked` finding).\n const counted = new Set<string>([\n ...servicesUnder(tree, sub).filter((s) => s.subsystem.length === sub.path.length).map((s) => s.id as string),\n ...subsystemsUnder(tree, sub).filter((c) => c.path.length === sub.path.length + 1).map((c) => c.name),\n ]);\n const strays = (await readdir(sub.dir, { withFileTypes: true }))\n .filter((e) => !e.name.startsWith(\".\") && e.name !== SUBSYSTEM_MARKER && !counted.has(e.name))\n .map((e) => `${entryIs(sub.dir, e, \"dir\") ? \"directory\" : \"file\"} ${e.name}`);\n const members = [\n ...services.map((s) => `service ${s}`),\n ...children.map((c) => `subsystem ${c}`),\n ...strays,\n ];\n if (members.length > 0) {\n fail(\n json,\n \"subsystem-not-empty\",\n `'${name}' still holds ${members.length} member(s): ${members.join(\", \")}. ` +\n \"`loam subsystem move <name>... --into <sub|.>` moves them out; a destructive command never picks targets you did not name.\",\n );\n return null;\n }\n const after: FleetTree = { ...tree, subsystems: tree.subsystems.filter((s) => s !== sub) };\n return {\n target: name,\n writes: [{ path: join(sub.dir, SUBSYSTEM_MARKER), content: null }],\n tree: after,\n what: \"removed\",\n report: async (views, recovered) => {\n // The directory itself, after the transaction: the marker is gone, the\n // group was verifiably empty, and an empty marker-less directory would\n // read as an unfiled service. Dotfile litter (.DS_Store) can still\n // defeat rmdir — reported, not swallowed.\n let directoryRemoved = true;\n try {\n await rmdir(sub.dir);\n } catch {\n directoryRemoved = !existsSync(sub.dir);\n }\n if (json) {\n emitJson({\n command: \"subsystem\",\n removed: name,\n path: repoPath(docsDir, sub.dir),\n directoryRemoved,\n views,\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`removed subsystem '${name}' (${repoPath(docsDir, sub.dir)}/)${reportViews(views)}`);\n if (!directoryRemoved) {\n console.log(\n `note: the directory itself could not be removed (something non-loam is still inside) — ` +\n `until it is deleted by hand it will list as an empty unfiled service.`,\n );\n }\n },\n };\n });\n}\n"]}