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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -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 +599 -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 +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -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 +252 -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 +204 -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 +270 -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 +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -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 +337 -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 +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/core/repo.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Read model of the docs repo — the single place that knows its layout.\n *\n * Enumeration goes by the filesystem, and only by the filesystem: files are the\n * source of truth, so `services/` IS the list of services and there is no\n * manifest that can disagree with it. Every command that needs \"which services\n * exist\", \"which features are in flight\" or \"where does artifact X live\" asks\n * here, so the layout is spelled exactly once.\n */\nimport { existsSync, statSync, type Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { listField, readFrontmatter, stringField } from \"./frontmatter.js\";\nimport type { FleetContext } from \"./fleet-context.js\";\nimport { serviceIdProblem } from \"./ids.js\";\nimport type { Finding } from \"./report.js\";\n\n/** Directory under features/ holding shipped features. Never a feature itself. */\nconst ARCHIVE_DIR = \"archive\";\n\nexport interface ServiceArtifacts {\n model: boolean;\n spec: boolean;\n openapi: boolean;\n /**\n * The async contract (AsyncAPI 3). Presence-tracked only, like `runbook` and\n * `health` — deliberately NOT part of the maturity ladder (core/maturity.ts),\n * whose `documented` rung is the model/spec/openapi triple. Adding a fourth\n * required artifact would demote every already-`documented` service in the\n * fleet on upgrade, without one byte of their files changing.\n */\n asyncapi: boolean;\n runbook: boolean;\n health: boolean;\n}\n\nexport interface ServiceEntry {\n /** Canonical service id — the directory name under services/. */\n id: string;\n /** Absolute path to services/<id>/. */\n dir: string;\n has: ServiceArtifacts;\n /** Number of ADR files under adrs/. */\n adrs: number;\n /** `status` from the living spec's frontmatter; null when nobody has said. */\n status: string | null;\n /**\n * Provenance signals from the living spec's frontmatter: whether it declares\n * any `sources`, and whether a `sources_digest` stamp exists. Presence only —\n * whether the digest still matches the code is `validate`'s question, and it\n * can only be answered from inside the service's own repo.\n */\n sources: { declared: boolean; stamped: boolean };\n /**\n * Why no loam command can author this directory, or absent when the name is a\n * legal service id — `serviceIdProblem`'s own sentence, so the read model and\n * the refusal say the same thing.\n *\n * The enumeration still LISTS it: the directory is there, it holds somebody's\n * documents, and hiding it would repeat the symlink mistake one field up. But\n * every authoring command (`adopt`, `delta`, `rebase`, `new --touches`,\n * `init`) refuses this id with `invalid-option`, so grading it as an ordinary\n * service left the fleet gate demanding fixes no loam command can make. The\n * NAME is the finding — see `serviceIdFindings`.\n */\n idProblem?: string;\n}\n\nexport interface FeatureEntry {\n /** Feature id, derived from the directory name (FEAT-1-split -> FEAT-1). */\n id: string;\n /** The directory name as it is on disk, slug and all. */\n dirName: string;\n /** Absolute path to the feature directory. */\n dir: string;\n archived: boolean;\n /** Services this feature carries a delta for, from specs/<svc>/. */\n services: string[];\n has: { intent: boolean; delta: boolean };\n}\n\n/**\n * Compare ids so digit runs sort numerically: FEAT-2 before FEAT-10.\n * Deterministic and locale-independent — ordering is part of the output\n * contract for `list`, and `--json` consumers diff it.\n */\nexport function compareIds(a: string, b: string): number {\n const ta = tokenize(a);\n const tb = tokenize(b);\n for (let i = 0; i < Math.max(ta.length, tb.length); i += 1) {\n const x = ta[i];\n const y = tb[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n if (typeof x === \"number\" && typeof y === \"number\") {\n if (x !== y) return x - y;\n } else if (String(x) !== String(y)) {\n return String(x) < String(y) ? -1 : 1;\n }\n }\n return 0;\n}\n\nfunction tokenize(s: string): (string | number)[] {\n return (s.match(/\\d+|\\D+/g) ?? []).map((t) => (/^\\d/.test(t) ? Number(t) : t));\n}\n\n/**\n * Known ids within a small edit distance of `id`, closest first, at most three.\n *\n * The did-you-mean hint for a name somebody typed: `loam new --touches` and\n * `loam delta <svc>` answer the same mistake, and each carried its own copy of\n * these twenty-four lines. It belongs here because the ids being scored are\n * this module's ids — service directory names, feature ids — and because\n * `compareIds` right above is the tiebreak that keeps the list deterministic\n * when two candidates score equally. Not in `core/ids.ts`: `compareIds` lives\n * here and repo.ts imports ids.js, so putting it there would close a cycle.\n *\n * `arch.ts`'s `closeIds` looks similar and is not: substring and prefix, capped\n * at five, answering \"which element could this be\" rather than \"did you\n * misspell a directory\". Two rules, two homes.\n */\nexport function nearestIds(id: string, known: string[]): string[] {\n const budget = Math.max(1, Math.floor(id.length / 4));\n return known\n .map((candidate) => ({ candidate, distance: editDistance(id.toLowerCase(), candidate.toLowerCase()) }))\n .filter((scored) => scored.distance <= budget)\n .sort((a, b) => a.distance - b.distance || compareIds(a.candidate, b.candidate))\n .slice(0, 3)\n .map((scored) => scored.candidate);\n}\n\n/** Plain Levenshtein — ids are short, and the row-at-a-time form keeps it obvious. */\nfunction editDistance(a: string, b: string): number {\n let previous = Array.from({ length: b.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i += 1) {\n const current = [i];\n for (let j = 1; j <= b.length; j += 1) {\n const substitution = previous[j - 1]! + (a[i - 1] === b[j - 1] ? 0 : 1);\n current.push(Math.min(previous[j]! + 1, current[j - 1]! + 1, substitution));\n }\n previous = current;\n }\n return previous[b.length]!;\n}\n\n/**\n * Feature id from a directory name: everything up to and including the first\n * number run (`FEAT-101-payment-splitting` -> `FEAT-101`). A name with no\n * `<word>-<number>` head is its own id. Quirk: a dated slug keeps only its first\n * segment (`release-2024-01-x` -> `release-2024`) — ids are not meant to be dates.\n */\nexport function featureIdFromDirName(dirName: string): string {\n const m = /^(.*?-\\d+)(?:-|$)/.exec(dirName);\n return m ? m[1]! : dirName;\n}\n\n/* ------------------------------------------------------------------ */\n/* Paths — artifact filenames are spelled here and nowhere else */\n/* ------------------------------------------------------------------ */\n\nexport interface ServicePaths {\n dir: string;\n model: string;\n spec: string;\n archSpec: string;\n openapi: string;\n /** The async contract — the event axis's sibling of `openapi`. */\n asyncapi: string;\n runbook: string;\n health: string;\n adrsDir: string;\n}\n\nexport function servicePaths(docsDir: string, service: string): ServicePaths {\n const dir = join(docsDir, \"services\", service);\n return {\n dir,\n model: join(dir, \"model.likec4\"),\n spec: join(dir, \"spec.md\"),\n archSpec: join(dir, \"arch.spec.md\"),\n openapi: join(dir, \"openapi.yaml\"),\n asyncapi: join(dir, \"asyncapi.yaml\"),\n runbook: join(dir, \"runbook.md\"),\n health: join(dir, \"health.yaml\"),\n adrsDir: join(dir, \"adrs\"),\n };\n}\n\n/**\n * The pair of requirement-carrying spec files, living and delta alike: the\n * business spec and the architecture spec. Same grammar, same delta algebra,\n * same merge — everything that walks \"the spec files\" of a service walks this\n * list, so the two axes cannot drift apart in what handles them. `key` indexes\n * ServicePaths/FeatureSpecPaths; `label` is how the axis names itself in prose.\n */\nexport const SPEC_AXES = [\n { key: \"spec\", file: \"spec.md\", label: \"requirements\" },\n { key: \"archSpec\", file: \"arch.spec.md\", label: \"arch requirements\" },\n] as const;\nexport type SpecAxis = (typeof SPEC_AXES)[number];\n\nexport interface FeaturePaths {\n dir: string;\n intent: string;\n delta: string;\n specsDir: string;\n adrsDir: string;\n}\n\nexport function featurePaths(featureDir: string): FeaturePaths {\n return {\n dir: featureDir,\n intent: join(featureDir, \"intent.md\"),\n delta: join(featureDir, \"delta.likec4\"),\n specsDir: join(featureDir, \"specs\"),\n adrsDir: join(featureDir, \"adrs\"),\n };\n}\n\nexport interface FeatureSpecPaths {\n dir: string;\n spec: string;\n archSpec: string;\n openapi: string;\n}\n\nexport function featureSpecPaths(featureDir: string, service: string): FeatureSpecPaths {\n const dir = join(featureDir, \"specs\", service);\n return {\n dir,\n spec: join(dir, \"spec.md\"),\n archSpec: join(dir, \"arch.spec.md\"),\n openapi: join(dir, \"openapi.yaml\"),\n };\n}\n\nexport function landscapePath(docsDir: string): string {\n return join(docsDir, \"architecture\", \"landscape.likec4\");\n}\n\nexport function featuresDir(docsDir: string): string {\n return join(docsDir, \"features\");\n}\n\n/**\n * The agent contract's filename, and where it sits in a docs repo.\n *\n * Both spellings are exported because the two questions are different: `init`,\n * `doctor` and `validate` want a path under a docsDir they already hold, while\n * `docs.ts` builds the scaffold as `[relative path, bytes]` pairs and needs the\n * bare name. Four modules spelled the literal out for themselves, and one of\n * them — `init` — uses it to decide whether a directory IS a docs repo at all,\n * so a rename that reached three of the four would leave `init` recognising a\n * file nothing else writes any more.\n *\n * `openspec-inventory.ts`'s \"AGENTS.md\" is deliberately NOT this constant: it\n * names a file in a foreign OpenSpec tree that happens to share the name.\n */\nexport const AGENTS_FILENAME = \"AGENTS.md\";\n\nexport function agentsPath(docsDir: string): string {\n return join(docsDir, AGENTS_FILENAME);\n}\n\n/**\n * Where shipped features live. `features/archive/` is spelled here and nowhere\n * else for the same reason every other artifact path is: `archive` and\n * `unarchive` move directories in and out of it, and a layout string that two\n * commands re-derive is a layout two commands can disagree about. The\n * `features/archive/…` strings in refusal PROSE are not this path — they are\n * what a reader types into `ls`, and they stay spelled out.\n */\nexport function archiveDir(docsDir: string): string {\n return join(featuresDir(docsDir), ARCHIVE_DIR);\n}\n\n/* ------------------------------------------------------------------ */\n/* Is there a docs repo there at all? */\n/* ------------------------------------------------------------------ */\n\n/**\n * What is actually at `docsDir`. Three answers, because they point at three\n * different fixes and the enumeration below used to give all of them the same\n * one — an empty list:\n *\n * - `missing` — nothing there (or not a directory): `docsDir` in loam.json\n * is wrong, or the docs repo was never cloned;\n * - `no-services` — a directory, but with no `services/`: it is some other\n * directory, most often the service repo itself after a typo;\n * - `ok` — a docs repo. `services/` may still be EMPTY, and that is a\n * legitimate state: a docs repo before the first `loam adopt`.\n *\n * \"Empty fleet\" and \"wrong path\" are the same output only if nobody asks this\n * question, and a green `loam list` over a docsDir that does not exist is worse\n * than a red one: it says the fleet is fine.\n */\nexport type DocsRepoKind = \"missing\" | \"no-services\" | \"ok\";\n\nexport interface DocsRepoState {\n kind: DocsRepoKind;\n /** The path examined, so a caller can quote it without re-deriving it. */\n path: string;\n}\n\nexport function docsRepoState(docsDir: string): DocsRepoState {\n const path = docsDir;\n let isDir = false;\n try {\n isDir = statSync(path).isDirectory();\n } catch {\n isDir = false;\n }\n if (!isDir) return { kind: \"missing\", path };\n return { kind: existsSync(join(path, \"services\")) ? \"ok\" : \"no-services\", path };\n}\n\n/**\n * The docs repo is not there. Thrown rather than returned because every caller\n * of the enumeration wants the same thing — to stop — and a `[]` return is the\n * bug this closes. `message` is written for a human at a terminal: the CLI's\n * top-level handler prints it verbatim, and commands that catch it report\n * `repository-unavailable`.\n */\nexport class DocsRepoUnavailableError extends Error {\n constructor(\n readonly state: DocsRepoState,\n message: string,\n ) {\n super(message);\n this.name = \"DocsRepoUnavailableError\";\n }\n}\n\n/**\n * Refuse to enumerate a docs repo that is not one. `allow` names the states the\n * caller can honestly survive: features/ may be absent from a real docs repo\n * (nothing is in flight yet), so feature enumeration only insists the docs repo\n * EXISTS, while service enumeration insists it is shaped like a docs repo — a\n * `services/` directory is what makes it one.\n */\nfunction requireDocsRepo(docsDir: string, allow: DocsRepoKind[]): void {\n const state = docsRepoState(docsDir);\n if (allow.includes(state.kind)) return;\n if (state.kind === \"missing\") {\n throw new DocsRepoUnavailableError(\n state,\n `The configured docs repo does not exist: ${state.path}. ` +\n \"Fix `docsDir` in loam.json, clone the docs repo there, \" +\n \"or run `loam init --docs <dir> --create` to make a new one.\",\n );\n }\n throw new DocsRepoUnavailableError(\n state,\n `${state.path} is not a docs repo — it has no services/ directory. ` +\n \"Point `docsDir` in loam.json at the shared docs repo, \" +\n \"or run `loam init --docs <dir> --create` to make one.\",\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Enumeration */\n/* ------------------------------------------------------------------ */\n\n/**\n * What a directory entry IS, following a symlink to find out.\n *\n * `readdir(withFileTypes)` does not follow symlinks: for a symlink `Dirent`\n * every `isDirectory()`/`isFile()` is false, whatever it points at. That made a\n * symlinked `services/<id>/` or `features/<id>/` vanish from every enumeration\n * at once — `list`, `validate --all`, the fleet gate — with no diagnostic\n * anywhere, which is the one outcome a shared docs repo cannot survive: the\n * fleet gate reported a service that is not there as fine because it never saw\n * it. Composing a docs repo out of symlinks (a worktree, a submodule, one\n * service's directory shared between two checkouts) is legitimate, so they are\n * FOLLOWED rather than refused. A dangling link stats as nothing and is skipped\n * exactly as an absent directory is — it is not a service, and saying so would\n * mean inventing an entry to hang the complaint on.\n */\nfunction entryIs(dir: string, e: Dirent, want: \"dir\" | \"file\"): boolean {\n if (!e.isSymbolicLink()) return want === \"dir\" ? e.isDirectory() : e.isFile();\n try {\n const s = statSync(join(dir, e.name));\n return want === \"dir\" ? s.isDirectory() : s.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Sorted names of the real subdirectories of `dir` (dot-dirs and files skipped).\n * A missing `dir` is an empty list — the swallow is deliberate but narrow: it\n * only ever runs INSIDE a docs repo that `requireDocsRepo` has already\n * confirmed, where an absent `features/` or `specs/` means \"none yet\" and\n * nothing else.\n */\nasync function subdirs(dir: string): Promise<string[]> {\n if (!existsSync(dir)) return [];\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((e) => !e.name.startsWith(\".\") && entryIs(dir, e, \"dir\"))\n .map((e) => e.name)\n .sort(compareIds);\n}\n\n/**\n * Markdown files sitting directly in `dir` — the ADR count, and the only place\n * that rule is spelled. Exported because `show` kept a second copy that tested\n * `Dirent.isFile()` itself: always false for a symlink, whatever it points at,\n * so a docs repo composed of symlinks (the layout `entryIs` above exists to\n * support) had `loam list` and `loam show` reporting different ADR counts for\n * the same service. One rule, one answer.\n */\nexport async function countMarkdown(dir: string): Promise<number> {\n if (!existsSync(dir)) return 0;\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.name.endsWith(\".md\") && entryIs(dir, e, \"file\")).length;\n}\n\n/** Services a feature carries a delta for, ordered — the subdirs of its specs/. */\nexport async function featureSpecServices(\n featureDir: string,\n context?: FleetContext,\n): Promise<string[]> {\n if (context !== undefined) return context.featureSpecServices(featureDir);\n return subdirs(featurePaths(featureDir).specsDir);\n}\n\n/** Every service in the docs repo, ordered by id. */\nexport async function listServices(docsDir: string, context?: FleetContext): Promise<ServiceEntry[]> {\n if (context !== undefined) return context.listServices(docsDir);\n requireDocsRepo(docsDir, [\"ok\"]);\n const names = await subdirs(join(docsDir, \"services\"));\n return Promise.all(\n names.map(async (id): Promise<ServiceEntry> => {\n const p = servicePaths(docsDir, id);\n const fm = await readFrontmatter(p.spec);\n const idProblem = serviceIdProblem(id, \"directory name\");\n return {\n id,\n dir: p.dir,\n ...(idProblem === null ? {} : { idProblem }),\n has: {\n model: existsSync(p.model),\n spec: existsSync(p.spec),\n openapi: existsSync(p.openapi),\n asyncapi: existsSync(p.asyncapi),\n runbook: existsSync(p.runbook),\n health: existsSync(p.health),\n },\n adrs: await countMarkdown(p.adrsDir),\n status: stringField(fm, \"status\") ?? null,\n sources: {\n declared: listField(fm, \"sources\").length > 0,\n stamped: stringField(fm, \"sources_digest\") !== undefined,\n },\n };\n }),\n );\n}\n\n/**\n * The directories under `services/` that no loam command can address.\n *\n * A finding rather than a silent skip, and an ERROR rather than a warning,\n * because the state is not survivable in either direction: `loam adopt`,\n * `loam delta`, `loam rebase` and `loam new --touches` all refuse the id with\n * `invalid-option`, so the documents in there can never be updated by loam\n * again, while `validate --all` used to grade the directory as an ordinary\n * service and demand `model.likec4`, a spec and a landscape binding for it. The\n * fix is a rename, and it is a rename loam cannot do for you — the id is\n * spelled in the landscape binding, in the frontmatter and in every feature\n * that touched the service — so the message names all three.\n *\n * Emitted from the fleet target (`validate --all`), where the SET of service\n * directories is the thing being checked; a per-service run is already refused\n * by its own `--service` guard before it gets here.\n */\nexport function serviceIdFindings(services: ServiceEntry[]): Finding[] {\n return services\n .filter((s) => s.idProblem !== undefined)\n .map((s) => ({\n severity: \"error\" as const,\n code: \"service.id-invalid\",\n subject: s.id,\n message:\n `services/${s.id}/ — ${s.idProblem} ` +\n `Every authoring command refuses this id (\\`loam adopt\\`, \\`loam delta\\`, \\`loam new --touches\\`), so nothing in that directory can be changed through loam. ` +\n `Rename the directory to a legal id, then update its \\`service:\\` frontmatter, its \\`metadata { service '${s.id}' }\\` binding in architecture/landscape.likec4, and any features/<FEAT>/specs/${s.id}/ that names it.`,\n }));\n}\n\nasync function readFeature(dir: string, dirName: string, archived: boolean): Promise<FeatureEntry> {\n const p = featurePaths(dir);\n return {\n id: featureIdFromDirName(dirName),\n dirName,\n dir,\n archived,\n services: await subdirs(p.specsDir),\n has: { intent: existsSync(p.intent), delta: existsSync(p.delta) },\n };\n}\n\n/**\n * Features in the docs repo, ordered by id (active and archived interleaved when\n * both are asked for). `features/archive/` is the archive, never a feature.\n */\nexport async function listFeatures(\n docsDir: string,\n opts: { includeArchived?: boolean } = {},\n context?: FleetContext,\n): Promise<FeatureEntry[]> {\n if (context !== undefined) return context.listFeatures(docsDir, opts);\n // \"no-services\" is tolerated here on purpose: a docs repo whose services/ is\n // gone is broken, but it is `listServices` (and `loam doctor`) that says so.\n // Refusing here too would turn one diagnosis into two contradictory ones.\n requireDocsRepo(docsDir, [\"ok\", \"no-services\"]);\n const root = featuresDir(docsDir);\n const active = (await subdirs(root)).filter((n) => n !== ARCHIVE_DIR);\n const out = await Promise.all(active.map((n) => readFeature(join(root, n), n, false)));\n\n if (opts.includeArchived) {\n const archiveRoot = join(root, ARCHIVE_DIR);\n const archived = await subdirs(archiveRoot);\n out.push(...(await Promise.all(archived.map((n) => readFeature(join(archiveRoot, n), n, true)))));\n }\n\n return out.sort((a, b) => compareIds(a.id, b.id) || compareIds(a.dirName, b.dirName));\n}\n\n/**\n * Which side of `features/archive/` a lookup may land on. The policy is an\n * argument, not a default, so every command's stance on shipped features is\n * visible at its call site: delta/validate/archive work on in-flight features\n * (\"exclude\"), show/verify/new read shipped ones too (\"include\"), unarchive\n * takes back nothing else (\"only\").\n */\nexport type ArchivedPolicy = \"exclude\" | \"include\" | \"only\";\n\n/**\n * Resolve a feature argument — a canonical id (`FEAT-5`) or an exact directory\n * name (`FEAT-5-slug`) — to its entry. The entry's `id` is the canonical\n * spelling, and it is the only name a caller may use from here on: tag filters,\n * coherence, and the cross-feature self-exclusion scans all compare against\n * `feature.id`, never against the raw argument.\n *\n * Matching is EXACT, on one of two spellings: the directory name as it is on\n * disk, or the canonical id derived from it. It used to also match any dirName\n * starting with `arg + \"-\"`, which made the argument a prefix query in\n * disguise: with FEAT-401 and FEAT-402 in flight, `loam archive FEAT` archived\n * whichever sorted first, and `loam delta billing` reached into\n * `billing-7-rewrite`. A destructive command must never pick a target the\n * caller did not name, so a prefix now resolves to nothing at all.\n *\n * An exact directory name still wins over the id spelling (`FEAT-5` the\n * directory over `FEAT-5-slug`, whose id is also `FEAT-5`), and under \"include\"\n * an active feature wins over an archived one. Ties among equally-exact\n * candidates go to the first by name — deterministic, unlike the raw readdir\n * order this replaces; `ambiguousFeatureMessage` lists them for the caller.\n */\nexport async function resolveFeature(\n docsDir: string,\n arg: string,\n archived: ArchivedPolicy,\n context?: FleetContext,\n): Promise<FeatureEntry | null> {\n return (await featureCandidates(docsDir, arg, archived, context))[0] ?? null;\n}\n\n/**\n * Every feature the argument names exactly, best first. Split out of\n * `resolveFeature` so a caller that wants to REFUSE an ambiguous argument can\n * see the tie instead of inheriting the winner.\n */\nexport async function featureCandidates(\n docsDir: string,\n arg: string,\n archived: ArchivedPolicy,\n context?: FleetContext,\n): Promise<FeatureEntry[]> {\n const all = await listFeatures(docsDir, { includeArchived: archived !== \"exclude\" }, context);\n return all\n .filter((f) => archived === \"include\" || f.archived === (archived === \"only\"))\n .filter((f) => f.dirName === arg || f.id === arg)\n .sort(\n (a, b) =>\n Number(b.dirName === arg) - Number(a.dirName === arg) ||\n Number(a.archived) - Number(b.archived) ||\n compareIds(a.dirName, b.dirName),\n );\n}\n\n/**\n * The message for an argument that names more than one feature — two directories\n * carrying the same id (`FEAT-6-aaa` and `FEAT-6-zzz`). `resolveFeature` still\n * picks one deterministically, but a command that is about to WRITE should say\n * which directories collided and make the caller spell one out.\n */\nexport function ambiguousFeatureMessage(arg: string, candidates: FeatureEntry[]): string {\n return (\n `'${arg}' names ${candidates.length} features: ${candidates.map((c) => c.dirName).join(\", \")}. ` +\n \"Pass the directory name you mean.\"\n );\n}\n\n/**\n * The message for an exclude-mode miss. When the feature exists under\n * `features/archive/`, \"no feature\" is a lie the caller would have to debug —\n * the honest answer is \"already archived\", plus how to look at it. The `--json`\n * code stays `unknown-target` either way: the target is unknown to the command\n * that asked, and the prose carries the diagnosis.\n */\nexport async function missingFeatureMessage(\n docsDir: string,\n arg: string,\n context?: FleetContext,\n): Promise<string> {\n const shipped = await resolveFeature(docsDir, arg, \"only\", context);\n if (shipped !== null) {\n return (\n `Feature '${shipped.id}' is already archived (features/archive/${shipped.dirName}) — ` +\n `\\`loam show ${shipped.id}\\` reads it, \\`loam unarchive ${shipped.id}\\` re-opens it.`\n );\n }\n return `No feature '${arg}' under ${featuresDir(docsDir)}.`;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/core/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAA6B,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AA8C1F,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAuB;IACjD,OAAO,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAuB,EAAE,QAAkB;IACvE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAU;IACpC,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QAC1D,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACpD,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAe;IACxC,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;QACrB,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;KACtC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The validation report — what `loam validate` produces before anything is\n * printed.\n *\n * Checks build findings; renderers turn them into a human view or into the JSON\n * contract. Keeping the two apart is what makes `--all` and `--json` possible\n * without a second implementation of every check, and it is why a finding\n * carries a stable `code`: prose is for people, codes are for callers.\n */\n\nexport type Severity = \"ok\" | \"warn\" | \"error\";\n\n/**\n * The glyph each severity is printed with. Five sites had written it out\n * themselves — four as a two-armed ternary that spelled `error` and let\n * everything else fall to ⚠ — and a reader who learns the marks from one\n * command's output has to be able to read them in the next. It lives beside\n * `Severity` because it is a fact about the union, not about any one renderer;\n * `TextHint.marker` above already describes it as \"the ✓/⚠/✗ marker\".\n */\nexport const SEVERITY_MARK: Record<Severity, string> = { ok: \"✓\", warn: \"⚠\", error: \"✗\" };\n\n/** How the text renderer should lay a finding out. The JSON contract ignores this. */\nexport interface TextHint {\n /** Leading spaces. Default 0. */\n indent?: number;\n /** Print the ✓/⚠/✗ marker. Default true. */\n marker?: boolean;\n /** Block header printed once, before the first finding that carries it. */\n header?: string;\n /** Prefix for each detail line (e.g. \"- \" for a list of names). */\n detailPrefix?: string;\n}\n\nexport interface Finding {\n severity: Severity;\n /** Stable machine identifier — prose may change, this may not. */\n code: string;\n message: string;\n /**\n * What the finding is about, when that is narrower than the target: the\n * service a feature's per-service check ran on, say. Without it a caller\n * would have to read the service name back out of the message.\n */\n subject?: string;\n details?: string[];\n /**\n * Coherence findings only: whether `loam archive` refuses on this issue\n * without `--approve` (issue.ts explains why severity alone cannot say).\n * Absent on findings that never face the archive gate.\n */\n gates?: boolean;\n text?: TextHint;\n}\n\nexport interface TargetReport {\n /**\n * What was validated. `landscape` is the fleet itself — the checks that need\n * every service and every element in view at once, so they belong to no single\n * service and only run under `--all`.\n */\n kind: \"service\" | \"feature\" | \"landscape\";\n id: string;\n findings: Finding[];\n}\n\n/** A target is valid when nothing in it is an error. Warnings never gate. */\nexport function targetValid(target: TargetReport): boolean {\n return !target.findings.some((f) => f.severity === \"error\");\n}\n\nexport function reportValid(targets: TargetReport[]): boolean {\n return targets.every(targetValid);\n}\n\nexport function countSeverity(targets: TargetReport[], severity: Severity): number {\n return targets.reduce((n, t) => n + t.findings.filter((f) => f.severity === severity).length, 0);\n}\n\nexport function findingJson(f: Finding): Record<string, unknown> {\n return {\n severity: f.severity,\n code: f.code,\n ...(f.subject === undefined ? {} : { subject: f.subject }),\n ...(f.gates === undefined ? {} : { gates: f.gates }),\n message: f.message,\n details: f.details ?? [],\n };\n}\n\nexport function targetJson(t: TargetReport): Record<string, unknown> {\n return {\n kind: t.kind,\n id: t.id,\n valid: targetValid(t),\n findings: t.findings.map(findingJson),\n };\n}\n"]}
package/dist/core/spec.js DELETED
@@ -1,505 +0,0 @@
1
- /**
2
- * Parser for OpenSpec-style requirement documents.
3
- *
4
- * Living specs use `## Requirements` with `### Requirement:` + `#### Scenario:`.
5
- * Feature deltas group requirements under `## ADDED|MODIFIED|REMOVED Requirements`.
6
- * Scenarios are the acceptance criteria (Given/When/Then) and the source for tests.
7
- */
8
- import { createHash } from "node:crypto";
9
- import { readFile } from "node:fs/promises";
10
- import { decodeDocument } from "./document-bytes.js";
11
- import { scenarioGherkin } from "./steps.js";
12
- export const KIND_RE = /^##\s+(ADDED|MODIFIED|REMOVED)\s+Requirements\s*$/i;
13
- const REQ_RE = /^###\s+Requirement:\s*(.+?)\s*$/;
14
- const SCN_RE = /^####\s+Scenario:\s*(.+?)\s*$/;
15
- const REQUIREMENT_ID_LINE_RE = /^\s*Requirement-ID:\s*(.*?)\s*$/i;
16
- const BASED_ON_LINE_RE = /^\s*Based-On:\s*(.*?)\s*$/i;
17
- /** Portable, review-friendly stable IDs. Case-sensitive by design. */
18
- export const REQUIREMENT_ID_RE = /^[A-Za-z][A-Za-z0-9._-]{0,127}$/;
19
- /**
20
- * How much of the sha256 a `Based-On:` line carries — the length
21
- * `sources_digest` already uses (core/provenance.ts), for one reason a reader
22
- * can hold: every digest loam writes into a document reads the same.
23
- */
24
- export const REQUIREMENT_DIGEST_LENGTH = 16;
25
- /** The value shape of a `Based-On:` line, exactly as `requirementDigest` writes it. */
26
- export const REQUIREMENT_DIGEST_RE = new RegExp(`^[0-9a-f]{${REQUIREMENT_DIGEST_LENGTH}}$`);
27
- /** Every authored Requirement-ID value in a requirement body, including blanks. */
28
- export function requirementIdDeclarations(requirement) {
29
- return requirement.text.flatMap((line) => {
30
- const match = REQUIREMENT_ID_LINE_RE.exec(line);
31
- return match === null ? [] : [match[1].trim()];
32
- });
33
- }
34
- /** Structural ID problems within one spec/delta document. */
35
- export function requirementIdProblems(reqs) {
36
- const problems = [];
37
- const owners = new Map();
38
- for (const requirement of reqs) {
39
- const declarations = requirementIdDeclarations(requirement);
40
- const values = declarations.length > 0
41
- ? declarations
42
- : requirement.id === undefined
43
- ? []
44
- : [requirement.id];
45
- if (declarations.length > 1) {
46
- problems.push({ kind: "repeated", requirement: requirement.name, values: declarations });
47
- }
48
- for (const value of values) {
49
- if (!REQUIREMENT_ID_RE.test(value)) {
50
- problems.push({ kind: "invalid", requirement: requirement.name, value });
51
- }
52
- }
53
- // A repeated declaration is already an error. Do not also pretend its
54
- // keep-last parse result is an unambiguous document identity.
55
- if (values.length !== 1 || !REQUIREMENT_ID_RE.test(values[0]))
56
- continue;
57
- const names = owners.get(values[0]) ?? [];
58
- names.push(requirement.name);
59
- owners.set(values[0], names);
60
- }
61
- for (const [id, requirements] of owners) {
62
- if (requirements.length > 1)
63
- problems.push({ kind: "duplicate", id, requirements });
64
- }
65
- return problems;
66
- }
67
- /** Every authored `Based-On:` value in a requirement body, including blanks. */
68
- export function basedOnDeclarations(requirement) {
69
- return requirement.text.flatMap((line) => {
70
- const match = BASED_ON_LINE_RE.exec(line);
71
- return match === null ? [] : [match[1].trim()];
72
- });
73
- }
74
- /**
75
- * The requirement without its baseline marker — what a digest is taken over,
76
- * and what archive merges into the living document.
77
- *
78
- * `Based-On:` is a statement ABOUT a delta ("this was written against that"),
79
- * never part of the requirement it describes, and both consequences of
80
- * forgetting that are bad. Left in the digest input, a requirement's digest
81
- * would depend on the pin pointing at it, so no baseline could ever be
82
- * self-consistent. Left in the merged text — `applyRequirementDelta` copies the
83
- * delta's body into living wholesale — the living document would grow a pin to
84
- * a version of itself, and the NEXT feature's baseline would be taken over a
85
- * document containing the previous feature's bookkeeping.
86
- */
87
- function withoutBaseline(requirement) {
88
- const { basedOn: _dropped, ...rest } = requirement;
89
- return { ...rest, text: requirement.text.filter((line) => !BASED_ON_LINE_RE.test(line)) };
90
- }
91
- /**
92
- * The identity of a requirement's CONTENT: sha256 over its canonical
93
- * serialization, truncated like every other digest loam stamps.
94
- *
95
- * Canonical, not raw bytes, and deliberately: a living spec is rewritten by
96
- * `serializeRequirements` on every archive, so hashing the file's bytes would
97
- * make a baseline go stale over reflowed blank lines — a false alarm on the one
98
- * check whose whole value is that it only fires when something real moved.
99
- * What the hash therefore covers is exactly what survives a round trip: the
100
- * heading, the body (`Requirement-ID:` and `Operations:`/`Covers:` lines
101
- * included), and every scenario with its Given/When/Then lines. `kind` and
102
- * `section` are not serialized and so are not hashed — a requirement does not
103
- * change because the document quoting it moved it under another heading.
104
- */
105
- export function requirementDigest(requirement) {
106
- return createHash("sha256")
107
- .update(serializeRequirements([withoutBaseline(requirement)]), "utf8")
108
- .digest("hex")
109
- .slice(0, REQUIREMENT_DIGEST_LENGTH);
110
- }
111
- /**
112
- * THE requirements section: the one H2 a living spec keeps its requirements
113
- * under, the one section `loam archive` rewrites, and the one heading a delta
114
- * may legally quote the living state beneath. One definition on purpose — the
115
- * archive cut point, archive's stray-requirement guard, and the delta-shape
116
- * exemption (core/delta.ts) used to spell this invariant three different ways,
117
- * and the cut point's substring `indexOf("\n## Requirements")` was a PREFIX
118
- * match that also hit `## Requirements Extra`. Full-line match on a trimmed
119
- * heading, with KIND_RE's case and interior-whitespace tolerance.
120
- */
121
- const REQUIREMENTS_SECTION_RE = /^##\s+Requirements\s*$/i;
122
- /** Is this H2 heading line (as sectionHeadings/`Requirement.section` spell it) the `## Requirements` heading? */
123
- export function isRequirementsHeading(heading) {
124
- return REQUIREMENTS_SECTION_RE.test(heading.trim());
125
- }
126
- /**
127
- * Track ``` / ~~~ fences line by line. Returns true while the line is fenced
128
- * content — including the fence marker itself — so heading-like lines inside a
129
- * code block are never mistaken for structure.
130
- */
131
- function fenceTracker() {
132
- let fence = null;
133
- return (line) => {
134
- const m = /^\s*(```|~~~)/.exec(line);
135
- if (m) {
136
- if (fence === null)
137
- fence = m[1];
138
- else if (fence === m[1])
139
- fence = null;
140
- return true;
141
- }
142
- return fence !== null;
143
- };
144
- }
145
- /**
146
- * Strip a leading UTF-8 BOM (U+FEFF), which editors on Windows write unasked.
147
- *
148
- * It has to happen here, at the two functions that take raw markdown, rather than
149
- * at each `readFile` call: those are spread across half the commands, so any one of
150
- * them could forget, and the failure is invisible. One byte was enough to void an
151
- * entire delta — `KIND_RE` is anchored `^##`, delta files routinely open with
152
- * `## MODIFIED Requirements` on line 1, and behind a BOM that line stopped being a
153
- * heading, so every requirement in the file fell back to BASE and
154
- * `applyRequirementDelta` skips BASE. Archive merged nothing and said nothing.
155
- * OpenSpec's own reader strips it for exactly this reason.
156
- *
157
- * Only at position 0: elsewhere U+FEFF is a zero-width no-break space and belongs
158
- * to the author's text.
159
- */
160
- function stripBom(md) {
161
- return md.charCodeAt(0) === 0xfeff ? md.slice(1) : md;
162
- }
163
- /** Every H2 heading outside a fenced block, with its 1-based line number. */
164
- export function sectionHeadings(md) {
165
- const fenced = fenceTracker();
166
- const out = [];
167
- stripBom(md).split(/\r?\n/).forEach((line, i) => {
168
- if (fenced(line))
169
- return;
170
- if (/^##\s+/.test(line) && !line.startsWith("###"))
171
- out.push({ text: line.trim(), line: i + 1 });
172
- });
173
- return out;
174
- }
175
- /**
176
- * Split a living spec around its requirements RUN — the stretch of
177
- * `### Requirement:` blocks inside the `## Requirements` section — so a merge
178
- * can rewrite exactly that and nothing else. All three slices reassemble the
179
- * input byte-for-byte (`head + run + tail === text`):
180
- *
181
- * - `head` — everything before the first requirement in the section: the
182
- * intro, the heading line itself, any prose under the heading.
183
- * - `run` — from the first `### Requirement:` line to the section's end (the
184
- * next `## ` heading, or EOF). Empty when the section holds no requirements.
185
- * - `tail` — everything from the section's end onward.
186
- *
187
- * Returns null when the document has no `## Requirements` heading at all.
188
- * Only the FIRST matching heading opens the section; a caller that must not
189
- * lose content behind a duplicate (archive) counts matches via
190
- * sectionHeadings() and refuses. Heading detection matches parseRequirements:
191
- * fenced lines are never structure, and a leading BOM hides no heading — but
192
- * the slices themselves are raw bytes, BOM and CRLF included.
193
- */
194
- export function splitRequirementsSection(text) {
195
- const fenced = fenceTracker();
196
- let inSection = false;
197
- let sectionStart = -1;
198
- let runStart = -1;
199
- let sectionEnd = text.length;
200
- let offset = 0;
201
- while (offset < text.length) {
202
- const nl = text.indexOf("\n", offset);
203
- const lineEnd = nl === -1 ? text.length : nl + 1;
204
- let line = text.slice(offset, nl === -1 ? text.length : nl);
205
- if (line.endsWith("\r"))
206
- line = line.slice(0, -1);
207
- // Position 0 only, mirroring stripBom: elsewhere U+FEFF is content.
208
- if (offset === 0 && line.charCodeAt(0) === 0xfeff)
209
- line = line.slice(1);
210
- if (!fenced(line)) {
211
- if (/^##\s+/.test(line) && !line.startsWith("###")) {
212
- if (inSection) {
213
- sectionEnd = offset;
214
- inSection = false;
215
- }
216
- else if (sectionStart === -1 && isRequirementsHeading(line)) {
217
- sectionStart = offset;
218
- inSection = true;
219
- }
220
- }
221
- else if (inSection && runStart === -1 && REQ_RE.test(line)) {
222
- runStart = offset;
223
- }
224
- }
225
- offset = lineEnd;
226
- }
227
- if (sectionStart === -1)
228
- return null;
229
- const cut = runStart === -1 ? sectionEnd : runStart;
230
- return { head: text.slice(0, cut), run: text.slice(cut, sectionEnd), tail: text.slice(sectionEnd) };
231
- }
232
- /**
233
- * A requirement document from disk as text — decoded, or refused by name.
234
- *
235
- * The read that belongs with the parser, because `readFile(path, "utf8")` and
236
- * `parseRequirements` together are a trap: a UTF-16 spec.md decodes to a string
237
- * in which no line matches `^###\s+Requirement:`, so the parser returns [] and
238
- * the document grades as an empty baseline instead of an unreadable one. Every
239
- * question loam asks of a spec — is it covered, does the delta merge, what does
240
- * the C4 spine touch — is then answered about a file nobody read.
241
- *
242
- * It THROWS (`NotUtf8DocumentError`, which names the path), unlike
243
- * `readFrontmatter` next door, because the two failures are not the same
244
- * failure. Frontmatter is read by the fleet enumeration, where one bad file
245
- * must not cost the other 119 services, so there it is a flag. A requirement
246
- * document is read by ONE target, and the caller is the only one who knows what
247
- * to do about it: a read-only command turns it into a finding on that target
248
- * (validate's `guarded`, which names the file and keeps grading the rest), a
249
- * command that writes refuses outright rather than computing over mojibake.
250
- */
251
- export async function readRequirementsDocument(path) {
252
- return decodeDocument(await readFile(path), path);
253
- }
254
- /** Parse all requirements (with their scenarios and delta kind) from a markdown doc. */
255
- export function parseRequirements(md) {
256
- const out = [];
257
- let kind = "BASE";
258
- let section;
259
- let req = null;
260
- let scn = null;
261
- const fenced = fenceTracker();
262
- for (const [index, line] of stripBom(md).split(/\r?\n/).entries()) {
263
- if (fenced(line)) {
264
- // Fenced content, marker included: body of whatever is open, never structure.
265
- if (scn)
266
- scn.lines.push(line);
267
- else if (req)
268
- req.text.push(line);
269
- continue;
270
- }
271
- // Any H2 heading ends the current requirement/scenario capture — section prose
272
- // must not leak into the previous scenario's body.
273
- if (/^##\s+/.test(line) && !line.startsWith("###")) {
274
- req = null;
275
- scn = null;
276
- section = line.trim();
277
- const mk = KIND_RE.exec(line);
278
- // A non-delta H2 (## Notes, ## Requirements…) starts an unrelated section:
279
- // requirements under it are plain BASE, not part of a stale delta section.
280
- kind = mk ? mk[1].toUpperCase() : "BASE";
281
- continue;
282
- }
283
- const mr = REQ_RE.exec(line);
284
- if (mr) {
285
- req = {
286
- kind,
287
- name: mr[1],
288
- text: [],
289
- operations: [],
290
- covers: [],
291
- publishes: [],
292
- consumes: [],
293
- scenarios: [],
294
- section,
295
- line: index + 1,
296
- };
297
- out.push(req);
298
- scn = null;
299
- continue;
300
- }
301
- const ms = SCN_RE.exec(line);
302
- if (ms && req) {
303
- scn = { name: ms[1], lines: [] };
304
- req.scenarios.push(scn);
305
- continue;
306
- }
307
- if (scn) {
308
- scn.lines.push(line);
309
- }
310
- else if (req) {
311
- req.text.push(line);
312
- const mi = REQUIREMENT_ID_LINE_RE.exec(line);
313
- if (mi)
314
- req.id = mi[1].trim();
315
- // Same keep-last quirk as Operations:/Covers: below — a repeated line is
316
- // a document problem, reported as one (`delta.baseline-invalid`), never
317
- // resolved differently by whoever happens to be reading.
318
- const mb = BASED_ON_LINE_RE.exec(line);
319
- if (mb)
320
- req.basedOn = mb[1].trim();
321
- const mo = /^\s*Operations?:\s*(.+?)\s*$/i.exec(line);
322
- if (mo)
323
- req.operations = mo[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
324
- // The Covers: mirror — same placement (requirement body, not a scenario),
325
- // same comma grammar, and deliberately the same quirk: a second line
326
- // REPLACES the first (assignment, not append), so the two lists can never
327
- // drift apart in how they read a repeated line.
328
- const mc = /^\s*Covers?:\s*(.+?)\s*$/i.exec(line);
329
- if (mc)
330
- req.covers = mc[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
331
- // The event axis, same grammar and the same keep-last quirk again. Note
332
- // what is NOT happening here: the line stays in `req.text` like every
333
- // other body line (the push above is unconditional), so it round-trips
334
- // through `serializeRequirements` and rides inside `requirementDigest`
335
- // exactly as `Operations:` does. That is what makes this parse purely
336
- // additive — no living document's digest moves, and no `Based-On:` pin
337
- // goes stale because loam learned to read one more line.
338
- const mp = /^\s*Publishes?:\s*(.+?)\s*$/i.exec(line);
339
- if (mp)
340
- req.publishes = mp[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
341
- const mcon = /^\s*Consumes?:\s*(.+?)\s*$/i.exec(line);
342
- if (mcon)
343
- req.consumes = mcon[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
344
- }
345
- }
346
- return out;
347
- }
348
- /** Requirements with no scenario — the OpenSpec coverage rule (every requirement needs ≥1). */
349
- export function requirementsMissingScenarios(reqs) {
350
- return reqs.filter((r) => r.kind !== "REMOVED" && r.scenarios.length === 0);
351
- }
352
- /**
353
- * Scenarios that satisfy the coverage rule above and test nothing.
354
- *
355
- * `requirementsMissingScenarios` counts HEADINGS, so a `#### Scenario:` with no
356
- * recognizable Given/When/Then line satisfied the fleet gate's coverage claim
357
- * outright — and it is not a hypothetical shape: it is what `loam new`
358
- * scaffolds and what an agent writes when it puts the criteria in prose.
359
- * Downstream the same emptiness is fatal twice over: cucumber runs a stepless
360
- * scenario vacuously green, and `loam verify --results` can never confirm it.
361
- *
362
- * The detector is `loam gherkin`'s own — `scenarioGherkin` decides what a step
363
- * IS, and a second opinion here would mean the gate and the emitted suite could
364
- * disagree about whether a scenario has any. That import is the one place these
365
- * two modules are circular (gherkin.ts parses requirements); both sides export
366
- * only function declarations, which are hoisted, so neither evaluation order
367
- * can observe a half-built module.
368
- */
369
- export function steplessScenarios(reqs) {
370
- const out = [];
371
- for (const r of reqs) {
372
- if (r.kind === "REMOVED")
373
- continue;
374
- for (const s of r.scenarios) {
375
- if (scenarioGherkin(s.lines).steps.length === 0)
376
- out.push({ requirement: r.name, scenario: s.name });
377
- }
378
- }
379
- return out;
380
- }
381
- /**
382
- * The stepless half of the coverage check, as findings — the missing-scenario
383
- * half stays where it is (`requirementsMissingScenarios`), because the two are
384
- * different states with different fixes: one requirement owes acceptance
385
- * criteria, the other owes STEPS in criteria somebody has already written.
386
- *
387
- * `label` is the coverage check's own label (`payment-service: requirements`),
388
- * so both halves of one document name it the same way.
389
- */
390
- export function steplessFindings(label, subject, reqs) {
391
- const bare = steplessScenarios(reqs);
392
- if (bare.length === 0)
393
- return [];
394
- return [
395
- {
396
- severity: "error",
397
- code: "requirements.stepless-scenario",
398
- subject,
399
- message: `${label}: ${bare.length} scenario(s) have a heading and no recognizable steps — ` +
400
- `they satisfy the coverage rule and test nothing. Cucumber runs a stepless scenario vacuously green and ` +
401
- `\`loam verify --results\` can never confirm it. Write the body as \`- **Given/When/Then**\` bullets.`,
402
- details: bare.map((s) => `${s.requirement} — ${s.scenario}`),
403
- text: { detailPrefix: "- " },
404
- },
405
- ];
406
- }
407
- /** Serialize requirements back to OpenSpec markdown (`### Requirement:` + `#### Scenario:`). */
408
- export function serializeRequirements(reqs) {
409
- // Framing (blank lines between sections) is normalized here; body content is
410
- // only edge-trimmed, never collapsed — blank lines inside a scenario (e.g. in
411
- // fenced code blocks) are verbatim content.
412
- const chunks = [];
413
- for (const r of reqs) {
414
- const chunk = [`### Requirement: ${r.name}`];
415
- const body = [...r.text];
416
- // Parsed documents already carry the authored line in `text`. This branch
417
- // also makes programmatically constructed Requirements serialize fully.
418
- if (r.id !== undefined && !body.some((line) => REQUIREMENT_ID_LINE_RE.test(line))) {
419
- body.unshift(`Requirement-ID: ${r.id}`);
420
- }
421
- // Directly under the identity it pins, which is where `loam rebase` writes
422
- // it too — the two lines are read together, so they are never apart.
423
- if (r.basedOn !== undefined && !body.some((line) => BASED_ON_LINE_RE.test(line))) {
424
- body.splice(body.findIndex((line) => REQUIREMENT_ID_LINE_RE.test(line)) + 1, 0, `Based-On: ${r.basedOn}`);
425
- }
426
- const text = body.join("\n").trim();
427
- if (text)
428
- chunk.push("", text);
429
- for (const s of r.scenarios) {
430
- chunk.push("", `#### Scenario: ${s.name}`);
431
- const body = s.lines.join("\n").trim();
432
- if (body)
433
- chunk.push(body);
434
- }
435
- chunks.push(chunk.join("\n"));
436
- }
437
- return chunks.join("\n\n").trim() + "\n";
438
- }
439
- /**
440
- * A delta requirement as it lands in the living document, with the bookkeeping
441
- * that belongs to the DELTA left behind: the `Based-On:` pin (a claim about
442
- * which living version this was written against — meaningless once it IS the
443
- * living version, and poison for the next feature's baseline, which would then
444
- * hash the previous feature's pin) and the line it was parsed from.
445
- */
446
- function asLiving(d) {
447
- const { line: _dropped, ...rest } = withoutBaseline(d);
448
- return { ...rest, kind: "BASE" };
449
- }
450
- /** Apply a feature's ADDED/MODIFIED/REMOVED requirements onto a living requirement set. */
451
- export function applyRequirementDelta(living, delta) {
452
- const malformed = [...requirementIdProblems(living), ...requirementIdProblems(delta)];
453
- if (malformed.length > 0) {
454
- throw new Error("cannot merge requirements with invalid, repeated, or duplicate Requirement-ID declarations");
455
- }
456
- let result = living.map((r) => ({ ...r, kind: "BASE" }));
457
- for (const d of delta) {
458
- // BASE is not a delta kind — a requirement outside an ADDED/MODIFIED/REMOVED
459
- // section (e.g. quoted under ## Notes) is documentation, not a change.
460
- if (d.kind === "BASE")
461
- continue;
462
- if (d.id !== undefined) {
463
- const idMatches = result
464
- .map((r, i) => (r.id === d.id ? i : -1))
465
- .filter((i) => i >= 0);
466
- const nameMatches = result
467
- .map((r, i) => (r.name === d.name ? i : -1))
468
- .filter((i) => i >= 0);
469
- const otherNameMatches = nameMatches.filter((i) => !idMatches.includes(i));
470
- if (idMatches.length > 1 || otherNameMatches.length > 0) {
471
- throw new Error(`ambiguous requirement identity for '${d.name}' (${d.id}): Requirement-ID and heading select different living requirements`);
472
- }
473
- if (d.kind === "REMOVED") {
474
- const selected = new Set(idMatches);
475
- result = result.filter((_, i) => !selected.has(i));
476
- }
477
- else {
478
- const merged = asLiving(d);
479
- if (idMatches.length === 1)
480
- result[idMatches[0]] = merged;
481
- else
482
- result.push(merged);
483
- }
484
- }
485
- else if (d.kind === "REMOVED") {
486
- result = result.filter((r) => r.name !== d.name);
487
- }
488
- else {
489
- const i = result.findIndex((r) => r.name === d.name);
490
- // A legacy delta may still modify an ID-bearing living requirement by
491
- // its exact heading. Keep the identity while the repository migrates.
492
- const inheritedId = i >= 0 ? result[i].id : undefined;
493
- const merged = {
494
- ...asLiving(d),
495
- ...(inheritedId === undefined ? {} : { id: inheritedId }),
496
- };
497
- if (i >= 0)
498
- result[i] = merged;
499
- else
500
- result.push(merged);
501
- }
502
- }
503
- return result;
504
- }
505
- //# sourceMappingURL=spec.js.map