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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1026) hide show
  1. package/CHANGELOG.md +2570 -61
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +494 -0
  8. package/SCHEMA.md +2362 -245
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +317 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +161 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +251 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +82 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +194 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +236 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +250 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +219 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/agent/agents-stamp.js +155 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +334 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/asyncapi/read.js +239 -0
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +47 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +282 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +88 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  398. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  399. package/dist/core/c4/parsed/specification.js +78 -0
  400. package/dist/core/c4/parsed/specification.js.map +1 -0
  401. package/dist/core/c4/parsed/values.js +57 -0
  402. package/dist/core/c4/parsed/values.js.map +1 -0
  403. package/dist/core/c4/parsed/view-ids.js +75 -0
  404. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  405. package/dist/core/c4/project/architecture.js +45 -0
  406. package/dist/core/c4/project/architecture.js.map +1 -0
  407. package/dist/core/c4/project/documents.js +58 -0
  408. package/dist/core/c4/project/documents.js.map +1 -0
  409. package/dist/core/c4/project/load.js +183 -0
  410. package/dist/core/c4/project/load.js.map +1 -0
  411. package/dist/core/c4/resolve/service.js +113 -0
  412. package/dist/core/c4/resolve/service.js.map +1 -0
  413. package/dist/core/c4/seed/fleet-file.js +201 -0
  414. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  415. package/dist/core/c4/seed/items.js +91 -0
  416. package/dist/core/c4/seed/items.js.map +1 -0
  417. package/dist/core/c4/seed/stamp.js +48 -0
  418. package/dist/core/c4/seed/stamp.js.map +1 -0
  419. package/dist/core/c4/seed/template.js +109 -0
  420. package/dist/core/c4/seed/template.js.map +1 -0
  421. package/dist/core/c4/source-mask.js +82 -0
  422. package/dist/core/c4/source-mask.js.map +1 -0
  423. package/dist/core/c4/source-scan.js +227 -0
  424. package/dist/core/c4/source-scan.js.map +1 -0
  425. package/dist/core/c4/splice/authored-source.js +101 -0
  426. package/dist/core/c4/splice/authored-source.js.map +1 -0
  427. package/dist/core/c4/splice/contract.js +10 -0
  428. package/dist/core/c4/splice/contract.js.map +1 -0
  429. package/dist/core/c4/splice/delta-blocks.js +126 -0
  430. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  431. package/dist/core/c4/splice/identity/edges.js +36 -0
  432. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  433. package/dist/core/c4/splice/landscape-merge.js +269 -0
  434. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  435. package/dist/core/c4/splice/placement.js +194 -0
  436. package/dist/core/c4/splice/placement.js.map +1 -0
  437. package/dist/core/c4/workspace.js +172 -0
  438. package/dist/core/c4/workspace.js.map +1 -0
  439. package/dist/core/capabilities/authoring/notes.js +141 -0
  440. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  441. package/dist/core/capabilities/capabilities.js +124 -0
  442. package/dist/core/capabilities/capabilities.js.map +1 -0
  443. package/dist/core/capabilities/delta/doc.js +50 -0
  444. package/dist/core/capabilities/delta/doc.js.map +1 -0
  445. package/dist/core/capabilities/delta/overlay.js +129 -0
  446. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  447. package/dist/core/capabilities/delta/summary.js +57 -0
  448. package/dist/core/capabilities/delta/summary.js.map +1 -0
  449. package/dist/core/capabilities/delta/tree.js +43 -0
  450. package/dist/core/capabilities/delta/tree.js.map +1 -0
  451. package/dist/core/capabilities/delta/uncovered.js +222 -0
  452. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  453. package/dist/core/capabilities/findings.js +342 -0
  454. package/dist/core/capabilities/findings.js.map +1 -0
  455. package/dist/core/capabilities/realizes/findings.js +181 -0
  456. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  457. package/dist/core/capabilities/realizes/join.js +143 -0
  458. package/dist/core/capabilities/realizes/join.js.map +1 -0
  459. package/dist/core/capabilities/rollup.js +199 -0
  460. package/dist/core/capabilities/rollup.js.map +1 -0
  461. package/dist/core/capabilities/tree.js +126 -0
  462. package/dist/core/capabilities/tree.js.map +1 -0
  463. package/dist/core/capabilities/usecase-join.js +277 -0
  464. package/dist/core/capabilities/usecase-join.js.map +1 -0
  465. package/dist/core/coherence/authoring/scaffold.js +177 -0
  466. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  467. package/dist/core/coherence/authoring/sentinels.js +74 -0
  468. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  469. package/dist/core/coherence/coherence.js +251 -0
  470. package/dist/core/coherence/coherence.js.map +1 -0
  471. package/dist/core/coherence/declared.js +269 -0
  472. package/dist/core/coherence/declared.js.map +1 -0
  473. package/dist/core/coherence/events/declared.js +206 -0
  474. package/dist/core/coherence/events/declared.js.map +1 -0
  475. package/dist/core/coherence/events/events.js +204 -0
  476. package/dist/core/coherence/events/events.js.map +1 -0
  477. package/dist/core/coherence/events/grades.js +166 -0
  478. package/dist/core/coherence/events/grades.js.map +1 -0
  479. package/dist/core/coherence/events/lookups.js +122 -0
  480. package/dist/core/coherence/events/lookups.js.map +1 -0
  481. package/dist/core/coherence/events/merged.js +40 -0
  482. package/dist/core/coherence/events/merged.js.map +1 -0
  483. package/dist/core/coherence/living.js +198 -0
  484. package/dist/core/coherence/living.js.map +1 -0
  485. package/dist/core/coherence/lookups.js +155 -0
  486. package/dist/core/coherence/lookups.js.map +1 -0
  487. package/dist/core/coherence/pending.js +39 -0
  488. package/dist/core/coherence/pending.js.map +1 -0
  489. package/dist/core/conflict-markers.js +60 -0
  490. package/dist/core/conflict-markers.js.map +1 -0
  491. package/dist/core/delta/claims.js +73 -0
  492. package/dist/core/delta/claims.js.map +1 -0
  493. package/dist/core/delta/delta.js +296 -0
  494. package/dist/core/delta/delta.js.map +1 -0
  495. package/dist/core/delta/document.js +177 -0
  496. package/dist/core/delta/document.js.map +1 -0
  497. package/dist/core/delta/scope.js +48 -0
  498. package/dist/core/delta/scope.js.map +1 -0
  499. package/dist/core/delta/select.js +237 -0
  500. package/dist/core/delta/select.js.map +1 -0
  501. package/dist/core/dependencies/dependencies.js +141 -0
  502. package/dist/core/dependencies/dependencies.js.map +1 -0
  503. package/dist/core/dependencies/facts.js +165 -0
  504. package/dist/core/dependencies/facts.js.map +1 -0
  505. package/dist/core/dependencies/fanin.js +88 -0
  506. package/dist/core/dependencies/fanin.js.map +1 -0
  507. package/dist/core/dependencies/graph.js +106 -0
  508. package/dist/core/dependencies/graph.js.map +1 -0
  509. package/dist/core/diff/base-git.js +148 -0
  510. package/dist/core/diff/base-git.js.map +1 -0
  511. package/dist/core/diff/base-state.js +209 -0
  512. package/dist/core/diff/base-state.js.map +1 -0
  513. package/dist/core/diff/current-state.js +96 -0
  514. package/dist/core/diff/current-state.js.map +1 -0
  515. package/dist/core/diff/scope/changed-paths.js +188 -0
  516. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  517. package/dist/core/diff/semantic.js +243 -0
  518. package/dist/core/diff/semantic.js.map +1 -0
  519. package/dist/core/diff/victims.js +247 -0
  520. package/dist/core/diff/victims.js.map +1 -0
  521. package/dist/core/docs.js +59 -55
  522. package/dist/core/docs.js.map +1 -1
  523. package/dist/core/doctor/agents.js +222 -0
  524. package/dist/core/doctor/agents.js.map +1 -0
  525. package/dist/core/doctor/config.js +135 -0
  526. package/dist/core/doctor/config.js.map +1 -0
  527. package/dist/core/doctor/doctor.js +275 -0
  528. package/dist/core/doctor/doctor.js.map +1 -0
  529. package/dist/core/doctor/report.js +13 -0
  530. package/dist/core/doctor/report.js.map +1 -0
  531. package/dist/core/doctor/residue.js +110 -0
  532. package/dist/core/doctor/residue.js.map +1 -0
  533. package/dist/core/document/apply.js +77 -0
  534. package/dist/core/document/apply.js.map +1 -0
  535. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  536. package/dist/core/document/frontmatter.js.map +1 -0
  537. package/dist/core/document/grammar/directives.js +143 -0
  538. package/dist/core/document/grammar/directives.js.map +1 -0
  539. package/dist/core/document/parse.js +263 -0
  540. package/dist/core/document/parse.js.map +1 -0
  541. package/dist/core/document/scenarios.js +221 -0
  542. package/dist/core/document/scenarios.js.map +1 -0
  543. package/dist/core/document/spec.js +241 -0
  544. package/dist/core/document/spec.js.map +1 -0
  545. package/dist/core/{config.js → envelope/config.js} +79 -18
  546. package/dist/core/envelope/config.js.map +1 -0
  547. package/dist/core/envelope/json.js +175 -0
  548. package/dist/core/envelope/json.js.map +1 -0
  549. package/dist/core/{version.js → envelope/version.js} +3 -3
  550. package/dist/core/envelope/version.js.map +1 -0
  551. package/dist/core/explain/families.js +175 -0
  552. package/dist/core/explain/families.js.map +1 -0
  553. package/dist/core/explain/fix-tables.js +178 -0
  554. package/dist/core/explain/fix-tables.js.map +1 -0
  555. package/dist/core/explain/lookup.js +181 -0
  556. package/dist/core/explain/lookup.js.map +1 -0
  557. package/dist/core/explain/refusals.js +49 -0
  558. package/dist/core/explain/refusals.js.map +1 -0
  559. package/dist/core/explain/terms.js +144 -0
  560. package/dist/core/explain/terms.js.map +1 -0
  561. package/dist/core/explore/describe.js +105 -0
  562. package/dist/core/explore/describe.js.map +1 -0
  563. package/dist/core/{explore.js → explore/explore.js} +50 -101
  564. package/dist/core/explore/explore.js.map +1 -0
  565. package/dist/core/fleet-context.js +132 -77
  566. package/dist/core/fleet-context.js.map +1 -1
  567. package/dist/core/gate/checks.js +201 -0
  568. package/dist/core/gate/checks.js.map +1 -0
  569. package/dist/core/gate/partners.js +205 -0
  570. package/dist/core/gate/partners.js.map +1 -0
  571. package/dist/core/gate/report.js +47 -0
  572. package/dist/core/gate/report.js.map +1 -0
  573. package/dist/core/gate/verification.js +113 -0
  574. package/dist/core/gate/verification.js.map +1 -0
  575. package/dist/core/gherkin/digest.js +60 -0
  576. package/dist/core/gherkin/digest.js.map +1 -0
  577. package/dist/core/gherkin/emit.js +177 -0
  578. package/dist/core/gherkin/emit.js.map +1 -0
  579. package/dist/core/gherkin/read.js +76 -0
  580. package/dist/core/gherkin/read.js.map +1 -0
  581. package/dist/core/gherkin/stale.js +234 -0
  582. package/dist/core/gherkin/stale.js.map +1 -0
  583. package/dist/core/gherkin/stamp.js +77 -0
  584. package/dist/core/gherkin/stamp.js.map +1 -0
  585. package/dist/core/gherkin/steps/catalogue.js +154 -0
  586. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  587. package/dist/core/gherkin/steps/inventory.js +98 -0
  588. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  589. package/dist/core/gherkin/steps/phrase.js +72 -0
  590. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  591. package/dist/core/glossary/delta.js +106 -0
  592. package/dist/core/glossary/delta.js.map +1 -0
  593. package/dist/core/glossary/tree.js +86 -0
  594. package/dist/core/glossary/tree.js.map +1 -0
  595. package/dist/core/kernel/concurrency.js.map +1 -0
  596. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  597. package/dist/core/kernel/document-bytes.js.map +1 -0
  598. package/dist/core/kernel/fences.js +42 -0
  599. package/dist/core/kernel/fences.js.map +1 -0
  600. package/dist/core/kernel/ids/capability.js +72 -0
  601. package/dist/core/kernel/ids/capability.js.map +1 -0
  602. package/dist/core/kernel/ids/dirs.js +33 -0
  603. package/dist/core/kernel/ids/dirs.js.map +1 -0
  604. package/dist/core/kernel/ids/feature.js +75 -0
  605. package/dist/core/kernel/ids/feature.js.map +1 -0
  606. package/dist/core/kernel/ids/service.js +156 -0
  607. package/dist/core/kernel/ids/service.js.map +1 -0
  608. package/dist/core/kernel/ids/subsystem.js +63 -0
  609. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  610. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  611. package/dist/core/kernel/path-safety.js.map +1 -0
  612. package/dist/core/kernel/records.js.map +1 -0
  613. package/dist/core/links/backlinks.js +68 -0
  614. package/dist/core/links/backlinks.js.map +1 -0
  615. package/dist/core/links/case.js +74 -0
  616. package/dist/core/links/case.js.map +1 -0
  617. package/dist/core/links/corpus.js +104 -0
  618. package/dist/core/links/corpus.js.map +1 -0
  619. package/dist/core/links/findings.js +110 -0
  620. package/dist/core/links/findings.js.map +1 -0
  621. package/dist/core/links/parse.js +236 -0
  622. package/dist/core/links/parse.js.map +1 -0
  623. package/dist/core/mcp/argv.js +111 -0
  624. package/dist/core/mcp/argv.js.map +1 -0
  625. package/dist/core/mcp/author-tools.js +72 -0
  626. package/dist/core/mcp/author-tools.js.map +1 -0
  627. package/dist/core/mcp/framing.js +46 -0
  628. package/dist/core/mcp/framing.js.map +1 -0
  629. package/dist/core/mcp/protocol.js +280 -0
  630. package/dist/core/mcp/protocol.js.map +1 -0
  631. package/dist/core/mcp/tools.js +314 -0
  632. package/dist/core/mcp/tools.js.map +1 -0
  633. package/dist/core/obligations/obligations.js +106 -0
  634. package/dist/core/obligations/obligations.js.map +1 -0
  635. package/dist/core/openapi/baseline/gate.js +166 -0
  636. package/dist/core/openapi/baseline/gate.js.map +1 -0
  637. package/dist/core/openapi/baseline/plan.js +112 -0
  638. package/dist/core/openapi/baseline/plan.js.map +1 -0
  639. package/dist/core/openapi/baseline/record.js +175 -0
  640. package/dist/core/openapi/baseline/record.js.map +1 -0
  641. package/dist/core/openapi/depth.js +122 -0
  642. package/dist/core/openapi/depth.js.map +1 -0
  643. package/dist/core/openapi/digest.js +189 -0
  644. package/dist/core/openapi/digest.js.map +1 -0
  645. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  646. package/dist/core/openapi/doc.js.map +1 -0
  647. package/dist/core/openapi/merge/components.js +234 -0
  648. package/dist/core/openapi/merge/components.js.map +1 -0
  649. package/dist/core/openapi/merge/error.js +26 -0
  650. package/dist/core/openapi/merge/error.js.map +1 -0
  651. package/dist/core/openapi/merge/markers.js +208 -0
  652. package/dist/core/openapi/merge/markers.js.map +1 -0
  653. package/dist/core/openapi/merge/merge.js +256 -0
  654. package/dist/core/openapi/merge/merge.js.map +1 -0
  655. package/dist/core/openapi/merge/pin.js +128 -0
  656. package/dist/core/openapi/merge/pin.js.map +1 -0
  657. package/dist/core/openspec/decide/capabilities.js +244 -0
  658. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  659. package/dist/core/openspec/decide/dispositions.js +63 -0
  660. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  661. package/dist/core/openspec/decide/renames.js +179 -0
  662. package/dist/core/openspec/decide/renames.js.map +1 -0
  663. package/dist/core/openspec/decide/verdict.js +84 -0
  664. package/dist/core/openspec/decide/verdict.js.map +1 -0
  665. package/dist/core/openspec/inventory.js +221 -0
  666. package/dist/core/openspec/inventory.js.map +1 -0
  667. package/dist/core/openspec/model/mapping.js +76 -0
  668. package/dist/core/openspec/model/mapping.js.map +1 -0
  669. package/dist/core/openspec/model/model.js +65 -0
  670. package/dist/core/openspec/model/model.js.map +1 -0
  671. package/dist/core/openspec/scan/changes.js +235 -0
  672. package/dist/core/openspec/scan/changes.js.map +1 -0
  673. package/dist/core/openspec/scan/config.js +200 -0
  674. package/dist/core/openspec/scan/config.js.map +1 -0
  675. package/dist/core/openspec/scan/shape.js +260 -0
  676. package/dist/core/openspec/scan/shape.js.map +1 -0
  677. package/dist/core/openspec/scan/walk.js +133 -0
  678. package/dist/core/openspec/scan/walk.js.map +1 -0
  679. package/dist/core/openspec/scan/workspace.js +71 -0
  680. package/dist/core/openspec/scan/workspace.js.map +1 -0
  681. package/dist/core/owners/codeowners.js +141 -0
  682. package/dist/core/owners/codeowners.js.map +1 -0
  683. package/dist/core/pack/features.js +120 -0
  684. package/dist/core/pack/features.js.map +1 -0
  685. package/dist/core/pack/joins.js +112 -0
  686. package/dist/core/pack/joins.js.map +1 -0
  687. package/dist/core/pack/living.js +118 -0
  688. package/dist/core/pack/living.js.map +1 -0
  689. package/dist/core/pack/pack.js +76 -0
  690. package/dist/core/pack/pack.js.map +1 -0
  691. package/dist/core/permissions/permissions.js +87 -0
  692. package/dist/core/permissions/permissions.js.map +1 -0
  693. package/dist/core/projection/api.js +84 -0
  694. package/dist/core/projection/api.js.map +1 -0
  695. package/dist/core/projection/arch-slice.js +63 -0
  696. package/dist/core/projection/arch-slice.js.map +1 -0
  697. package/dist/core/projection/events.js +34 -0
  698. package/dist/core/projection/events.js.map +1 -0
  699. package/dist/core/provenance/findings.js +208 -0
  700. package/dist/core/provenance/findings.js.map +1 -0
  701. package/dist/core/provenance/git.js +225 -0
  702. package/dist/core/provenance/git.js.map +1 -0
  703. package/dist/core/provenance/gitq/moves.js +182 -0
  704. package/dist/core/provenance/gitq/moves.js.map +1 -0
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  707. package/dist/core/provenance/sample/scope.js +238 -0
  708. package/dist/core/provenance/sample/scope.js.map +1 -0
  709. package/dist/core/provenance/sample/sections.js +61 -0
  710. package/dist/core/provenance/sample/sections.js.map +1 -0
  711. package/dist/core/provenance/sources.js +263 -0
  712. package/dist/core/provenance/sources.js.map +1 -0
  713. package/dist/core/provenance/stamp.js +232 -0
  714. package/dist/core/provenance/stamp.js.map +1 -0
  715. package/dist/core/provenance/walk.js +157 -0
  716. package/dist/core/provenance/walk.js.map +1 -0
  717. package/dist/core/repo/authored/paths.js +192 -0
  718. package/dist/core/repo/authored/paths.js.map +1 -0
  719. package/dist/core/repo/entries.js +126 -0
  720. package/dist/core/repo/entries.js.map +1 -0
  721. package/dist/core/repo/paths.js +236 -0
  722. package/dist/core/repo/paths.js.map +1 -0
  723. package/dist/core/repo/repo.js +248 -0
  724. package/dist/core/repo/repo.js.map +1 -0
  725. package/dist/core/repo/service-target.js +153 -0
  726. package/dist/core/repo/service-target.js.map +1 -0
  727. package/dist/core/repo/state.js +58 -0
  728. package/dist/core/repo/state.js.map +1 -0
  729. package/dist/core/repo/tree/find.js +52 -0
  730. package/dist/core/repo/tree/find.js.map +1 -0
  731. package/dist/core/repo/tree/fs.js +64 -0
  732. package/dist/core/repo/tree/fs.js.map +1 -0
  733. package/dist/core/repo/tree/marker.js +61 -0
  734. package/dist/core/repo/tree/marker.js.map +1 -0
  735. package/dist/core/repo/tree/views.js +172 -0
  736. package/dist/core/repo/tree/views.js.map +1 -0
  737. package/dist/core/repo/tree/walk.js +234 -0
  738. package/dist/core/repo/tree/walk.js.map +1 -0
  739. package/dist/core/results.js +99 -2
  740. package/dist/core/results.js.map +1 -1
  741. package/dist/core/scaffold/landscape.js +131 -0
  742. package/dist/core/scaffold/landscape.js.map +1 -0
  743. package/dist/core/scaffold/readme.js +84 -0
  744. package/dist/core/scaffold/readme.js.map +1 -0
  745. package/dist/core/staging/commit.js +241 -0
  746. package/dist/core/staging/commit.js.map +1 -0
  747. package/dist/core/staging/interrupted.js +96 -0
  748. package/dist/core/staging/interrupted.js.map +1 -0
  749. package/dist/core/staging/lock.js +206 -0
  750. package/dist/core/staging/lock.js.map +1 -0
  751. package/dist/core/staging/recovery/finding.js +35 -0
  752. package/dist/core/staging/recovery/finding.js.map +1 -0
  753. package/dist/core/staging/recovery/intent.js +98 -0
  754. package/dist/core/staging/recovery/intent.js.map +1 -0
  755. package/dist/core/staging/recovery/recover.js +126 -0
  756. package/dist/core/staging/recovery/recover.js.map +1 -0
  757. package/dist/core/staging/recovery/residue.js +62 -0
  758. package/dist/core/staging/recovery/residue.js.map +1 -0
  759. package/dist/core/staging/snapshot.js +202 -0
  760. package/dist/core/staging/snapshot.js.map +1 -0
  761. package/dist/core/staging/txn/forward.js +171 -0
  762. package/dist/core/staging/txn/forward.js.map +1 -0
  763. package/dist/core/staging/txn/journal.js +139 -0
  764. package/dist/core/staging/txn/journal.js.map +1 -0
  765. package/dist/core/staging/txn/transaction.js +75 -0
  766. package/dist/core/staging/txn/transaction.js.map +1 -0
  767. package/dist/core/staging/writes.js +113 -0
  768. package/dist/core/staging/writes.js.map +1 -0
  769. package/dist/core/status/actions/execution.js +49 -0
  770. package/dist/core/status/actions/execution.js.map +1 -0
  771. package/dist/core/status/contracts.js +102 -0
  772. package/dist/core/status/contracts.js.map +1 -0
  773. package/dist/core/status/feature/artifacts.js +171 -0
  774. package/dist/core/status/feature/artifacts.js.map +1 -0
  775. package/dist/core/status/feature/feature.js +207 -0
  776. package/dist/core/status/feature/feature.js.map +1 -0
  777. package/dist/core/status/feature/next.js +150 -0
  778. package/dist/core/status/feature/next.js.map +1 -0
  779. package/dist/core/status/feature/state.js +21 -0
  780. package/dist/core/status/feature/state.js.map +1 -0
  781. package/dist/core/status/fleet/fleet.js +187 -0
  782. package/dist/core/status/fleet/fleet.js.map +1 -0
  783. package/dist/core/status/fleet/next.js +207 -0
  784. package/dist/core/status/fleet/next.js.map +1 -0
  785. package/dist/core/status/interrupted.js +97 -0
  786. package/dist/core/status/interrupted.js.map +1 -0
  787. package/dist/core/status/report.js +31 -0
  788. package/dist/core/status/report.js.map +1 -0
  789. package/dist/core/status/scan.js +41 -0
  790. package/dist/core/status/scan.js.map +1 -0
  791. package/dist/core/status/verification.js +201 -0
  792. package/dist/core/status/verification.js.map +1 -0
  793. package/dist/core/usecases/capability.js +169 -0
  794. package/dist/core/usecases/capability.js.map +1 -0
  795. package/dist/core/usecases/delta/claims.js +116 -0
  796. package/dist/core/usecases/delta/claims.js.map +1 -0
  797. package/dist/core/usecases/delta/flows.js +103 -0
  798. package/dist/core/usecases/delta/flows.js.map +1 -0
  799. package/dist/core/usecases/delta/overlay.js +235 -0
  800. package/dist/core/usecases/delta/overlay.js.map +1 -0
  801. package/dist/core/usecases/fleet.js +137 -0
  802. package/dist/core/usecases/fleet.js.map +1 -0
  803. package/dist/core/usecases/operations.js +111 -0
  804. package/dist/core/usecases/operations.js.map +1 -0
  805. package/dist/core/usecases/place.js +44 -0
  806. package/dist/core/usecases/place.js.map +1 -0
  807. package/dist/core/usecases/touch.js +101 -0
  808. package/dist/core/usecases/touch.js.map +1 -0
  809. package/dist/core/verify/answers.js +173 -0
  810. package/dist/core/verify/answers.js.map +1 -0
  811. package/dist/core/verify/build.js +150 -0
  812. package/dist/core/verify/build.js.map +1 -0
  813. package/dist/core/verify/checklist.js +201 -0
  814. package/dist/core/verify/checklist.js.map +1 -0
  815. package/dist/core/verify/claims/identity.js +61 -0
  816. package/dist/core/verify/claims/identity.js.map +1 -0
  817. package/dist/core/verify/cross/diff.js +97 -0
  818. package/dist/core/verify/cross/diff.js.map +1 -0
  819. package/dist/core/verify/evidence/contract.js +169 -0
  820. package/dist/core/verify/evidence/contract.js.map +1 -0
  821. package/dist/core/verify/file.js +257 -0
  822. package/dist/core/verify/file.js.map +1 -0
  823. package/dist/core/verify/pins/lint.js +157 -0
  824. package/dist/core/verify/pins/lint.js.map +1 -0
  825. package/dist/core/verify/pins/pin.js +95 -0
  826. package/dist/core/verify/pins/pin.js.map +1 -0
  827. package/dist/core/verify/record.js +142 -0
  828. package/dist/core/verify/record.js.map +1 -0
  829. package/dist/core/verify/store/commit.js +89 -0
  830. package/dist/core/verify/store/commit.js.map +1 -0
  831. package/dist/core/verify/store/render.js +57 -0
  832. package/dist/core/verify/store/render.js.map +1 -0
  833. package/dist/core/vocabulary/health.js +91 -0
  834. package/dist/core/vocabulary/health.js.map +1 -0
  835. package/dist/core/vocabulary/issue.js +55 -0
  836. package/dist/core/vocabulary/issue.js.map +1 -0
  837. package/dist/core/vocabulary/maturity.js +145 -0
  838. package/dist/core/vocabulary/maturity.js.map +1 -0
  839. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  840. package/dist/core/vocabulary/report.js.map +1 -0
  841. package/dist/core/vocabulary/steps.js +266 -0
  842. package/dist/core/vocabulary/steps.js.map +1 -0
  843. package/dist/core/workspace/discover.js +239 -0
  844. package/dist/core/workspace/discover.js.map +1 -0
  845. package/dist/core/workspace/render.js +52 -0
  846. package/dist/core/workspace/render.js.map +1 -0
  847. package/examples/README.md +189 -0
  848. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  849. package/examples/docs/architecture/capabilities.yaml +31 -0
  850. package/examples/docs/architecture/landscape.likec4 +280 -0
  851. package/examples/docs/architecture/obligations.yaml +23 -0
  852. package/examples/docs/architecture/permissions.yaml +50 -0
  853. package/examples/docs/architecture/subsystems.likec4 +11 -0
  854. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  855. package/examples/docs/capabilities/checkout/spec.md +55 -0
  856. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  857. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  858. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  859. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  860. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  863. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  864. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  865. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  866. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  867. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  868. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  869. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  870. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  871. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  872. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  873. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  874. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  875. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  876. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  877. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  878. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  879. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  882. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  883. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  884. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  885. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  886. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  887. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  888. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  889. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  890. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  891. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  896. package/examples/docs/glossary/order.md +15 -0
  897. package/examples/docs/glossary/payments/authorization.md +14 -0
  898. package/examples/docs/likec4.config.json +9 -0
  899. package/examples/docs/loam.json +3 -0
  900. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  901. package/examples/docs/services/checkout-web/spec.md +91 -0
  902. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  903. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  904. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  905. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  906. package/examples/docs/services/order-service/arch.spec.md +127 -0
  907. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  908. package/examples/docs/services/order-service/health.yaml +57 -0
  909. package/examples/docs/services/order-service/model.likec4 +138 -0
  910. package/examples/docs/services/order-service/openapi.yaml +257 -0
  911. package/examples/docs/services/order-service/runbook.md +62 -0
  912. package/examples/docs/services/order-service/spec.md +134 -0
  913. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  914. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  915. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  916. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  917. package/examples/docs/services/payment-service/health.yaml +39 -0
  918. package/examples/docs/services/payment-service/model.likec4 +67 -0
  919. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  920. package/examples/docs/services/payment-service/runbook.md +37 -0
  921. package/examples/docs/services/payment-service/spec.md +88 -0
  922. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  923. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  924. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  925. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  926. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  927. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  928. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  929. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  930. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  931. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  932. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  933. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  934. package/examples/docs/services/platform/subsystem.yaml +2 -0
  935. package/examples/fleet.yaml +57 -0
  936. package/package.json +19 -6
  937. package/dist/commands/adopt.js +0 -240
  938. package/dist/commands/adopt.js.map +0 -1
  939. package/dist/commands/archive.js +0 -1313
  940. package/dist/commands/archive.js.map +0 -1
  941. package/dist/commands/delta.js +0 -388
  942. package/dist/commands/delta.js.map +0 -1
  943. package/dist/commands/docs-repo-gate.js.map +0 -1
  944. package/dist/commands/format.js +0 -17
  945. package/dist/commands/format.js.map +0 -1
  946. package/dist/commands/gherkin.js +0 -309
  947. package/dist/commands/gherkin.js.map +0 -1
  948. package/dist/commands/init.js.map +0 -1
  949. package/dist/commands/list.js +0 -291
  950. package/dist/commands/list.js.map +0 -1
  951. package/dist/commands/migrate-openspec.js +0 -950
  952. package/dist/commands/migrate-openspec.js.map +0 -1
  953. package/dist/commands/new.js +0 -413
  954. package/dist/commands/new.js.map +0 -1
  955. package/dist/commands/rebase.js +0 -356
  956. package/dist/commands/rebase.js.map +0 -1
  957. package/dist/commands/show.js.map +0 -1
  958. package/dist/commands/status.js +0 -254
  959. package/dist/commands/status.js.map +0 -1
  960. package/dist/commands/unarchive.js.map +0 -1
  961. package/dist/commands/validate.js +0 -1591
  962. package/dist/commands/validate.js.map +0 -1
  963. package/dist/commands/verify.js +0 -775
  964. package/dist/commands/verify.js.map +0 -1
  965. package/dist/commands/vouch.js +0 -343
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2065
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js +0 -68
  970. package/dist/core/agents-stamp.js.map +0 -1
  971. package/dist/core/arch.js +0 -130
  972. package/dist/core/arch.js.map +0 -1
  973. package/dist/core/brief.js +0 -733
  974. package/dist/core/brief.js.map +0 -1
  975. package/dist/core/coherence.js +0 -577
  976. package/dist/core/coherence.js.map +0 -1
  977. package/dist/core/concurrency.js.map +0 -1
  978. package/dist/core/config.js.map +0 -1
  979. package/dist/core/delta.js +0 -476
  980. package/dist/core/delta.js.map +0 -1
  981. package/dist/core/dependencies.js +0 -356
  982. package/dist/core/dependencies.js.map +0 -1
  983. package/dist/core/doctor.js +0 -559
  984. package/dist/core/doctor.js.map +0 -1
  985. package/dist/core/document-bytes.js.map +0 -1
  986. package/dist/core/explore.js.map +0 -1
  987. package/dist/core/frontmatter.js.map +0 -1
  988. package/dist/core/gherkin.js +0 -463
  989. package/dist/core/gherkin.js.map +0 -1
  990. package/dist/core/health.js +0 -67
  991. package/dist/core/health.js.map +0 -1
  992. package/dist/core/ids.js +0 -122
  993. package/dist/core/ids.js.map +0 -1
  994. package/dist/core/issue.js +0 -9
  995. package/dist/core/issue.js.map +0 -1
  996. package/dist/core/json.js +0 -74
  997. package/dist/core/json.js.map +0 -1
  998. package/dist/core/likec4.js +0 -479
  999. package/dist/core/likec4.js.map +0 -1
  1000. package/dist/core/maturity.js +0 -66
  1001. package/dist/core/maturity.js.map +0 -1
  1002. package/dist/core/openapi-merge.js +0 -552
  1003. package/dist/core/openapi-merge.js.map +0 -1
  1004. package/dist/core/openapi.js.map +0 -1
  1005. package/dist/core/openspec-inventory.js +0 -1440
  1006. package/dist/core/openspec-inventory.js.map +0 -1
  1007. package/dist/core/path-safety.js.map +0 -1
  1008. package/dist/core/provenance.js +0 -720
  1009. package/dist/core/provenance.js.map +0 -1
  1010. package/dist/core/records.js.map +0 -1
  1011. package/dist/core/repo.js +0 -436
  1012. package/dist/core/repo.js.map +0 -1
  1013. package/dist/core/report.js.map +0 -1
  1014. package/dist/core/spec.js +0 -481
  1015. package/dist/core/spec.js.map +0 -1
  1016. package/dist/core/staging.js +0 -890
  1017. package/dist/core/staging.js.map +0 -1
  1018. package/dist/core/status.js +0 -1104
  1019. package/dist/core/status.js.map +0 -1
  1020. package/dist/core/steps.js +0 -72
  1021. package/dist/core/steps.js.map +0 -1
  1022. package/dist/core/verify.js +0 -807
  1023. package/dist/core/verify.js.map +0 -1
  1024. package/dist/core/version.js.map +0 -1
  1025. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1026. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The fleet's use cases, read once for whoever asks — and NOT read at all when
3
+ * the fleet demonstrably has none.
4
+ *
5
+ * `validate --all` grades use cases as part of a run that was already loading
6
+ * `architecture/` as a LikeC4 project, so it pays nothing extra. Every reader
7
+ * added in phase 4 is in a different position: `loam diff`, `loam delta`,
8
+ * `loam context` and `loam explore` did not load the fleet map as a project
9
+ * before, and `loam delta` in particular sits in `/loam-implement`'s inner loop
10
+ * — a Langium workspace spin-up there is felt on every iteration of somebody's
11
+ * day. So this module's whole job is to answer "which use cases does the fleet
12
+ * declare?" and, in the common case where the answer is none, to answer it
13
+ * without starting LikeC4 at all.
14
+ *
15
+ * THE CHEAP GATE IS A BYTE SCAN, and it is sound rather than heuristic. A view
16
+ * is a use case only if it carries a reserved tag — `#cap-<slug>` or
17
+ * `#req-<slug>`, `isUseCase` below is the whole test — and LikeC4 refuses an
18
+ * undeclared tag, so a fleet with a use case has the literal text `cap-` or
19
+ * `req-` in the file that declares the view AND in the file that declares the
20
+ * tag. A document set in which no file contains either substring therefore
21
+ * declares no use case, and `{ views: [] }` is the correct answer, not an
22
+ * evasion. The scan reads bytes rather than decoded text on purpose: it is
23
+ * looking for an ASCII substring, and `readFile(path, "utf8")` on a document
24
+ * that is not UTF-8 would substitute U+FFFD and could hide it.
25
+ *
26
+ * BOTH PREFIXES, because the gate and the predicate answer one question and a
27
+ * gate narrower than the predicate is silence rather than a smaller answer. A
28
+ * fleet whose only flow is tagged `#req-` alone declares `tag req-…` and no
29
+ * `cap-` anywhere, so a `cap-`-only scan returned `false` and none of the five
30
+ * readers ever loaded the project — the widening below would have fixed nothing
31
+ * without this one.
32
+ *
33
+ * The gate FAILS CLOSED. A document this module cannot open is treated as one
34
+ * that might mention the prefix, so the project load happens and reports the
35
+ * failure as a hole. The alternative — reading an EACCES as "no tag here" — is
36
+ * the fail-open shape docs/CODE-STYLE.md forbids at a validator, arriving in an
37
+ * optimisation.
38
+ */
39
+ import { readFile } from "node:fs/promises";
40
+ import { CAP_TAG_PREFIX, REQ_TAG_PREFIX } from "../capabilities/usecase-join.js";
41
+ import { architectureDir, loadArchitecture } from "../c4/project/architecture.js";
42
+ import { architectureDocuments } from "../c4/project/documents.js";
43
+ import { serviceResolver } from "../c4/resolve/service.js";
44
+ import { subsystemViewsPath } from "../repo/paths.js";
45
+ /**
46
+ * Does any document in the set even mention a reserved tag prefix?
47
+ *
48
+ * Sequential and short-circuiting ON PURPOSE — the usual "no await in a loop"
49
+ * rule is about independent work that all has to finish, and this loop exists
50
+ * precisely so that a fleet whose first file declares a use case stops reading
51
+ * the other ninety-nine.
52
+ *
53
+ * `req-` is the looser of the two substrings and that is accepted knowingly: a
54
+ * document mentioning it in prose costs one project load loam would otherwise
55
+ * have skipped, which is a slower right answer. Dropping it costs a wrong one.
56
+ */
57
+ async function mentionsTagPrefix(paths) {
58
+ for (const path of paths) {
59
+ try {
60
+ const bytes = await readFile(path);
61
+ if (bytes.includes(CAP_TAG_PREFIX) || bytes.includes(REQ_TAG_PREFIX))
62
+ return true;
63
+ }
64
+ catch {
65
+ // Unreadable is not "no tag": the project load below owns that failure and
66
+ // will report it as a hole naming the document. Answering `false` here
67
+ // would let an unopenable file grade the fleet as having no use cases.
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+ /**
74
+ * Is this a use case at all? — the ONE answer, for `validate` and every reader
75
+ * alike.
76
+ *
77
+ * EITHER RESERVED PREFIX OPTS A VIEW IN. A view carrying `#req-CHK-ONCE` and no
78
+ * `#cap-` tag is an author who asked to be graded and forgot half the claim; it
79
+ * earns `usecase.requirement-unresolved`'s `unscoped` arm rather than silence,
80
+ * and SCHEMA.md has said so since the tag shipped.
81
+ *
82
+ * It is exported because it was the asymmetry, not the rule, that was the
83
+ * defect. `validate --all` widened here and the readers did not, so a
84
+ * `#req-`-only flow was graded hop by hop by the gate and invisible to `loam
85
+ * diff`, `delta`, `status`, the packs and `list capabilities` — a fleet where
86
+ * the gate and the report disagree about what exists, which is worse than
87
+ * either answer alone. One predicate is what stops that recurring: a caller
88
+ * that spells `startsWith` itself is a second answer waiting to drift.
89
+ *
90
+ * The parameter is the smallest shape the test needs rather than `ParsedView`,
91
+ * so a caller holding a view-like row does not have to build one to ask. Both
92
+ * prefix tests match their resolver's exactly, case included, so a view can
93
+ * never be opted in here and read as untagged in `resolveCapabilityTags`.
94
+ *
95
+ * NOT the same question as `flowsClaiming`'s. That one asks which flows claim
96
+ * capability X and matches the whole `cap-<slug>` tag; a `#req-`-only view
97
+ * claims none, and it is correct for it to stay out of that answer while
98
+ * counting as a use case here.
99
+ */
100
+ export function isUseCase(view) {
101
+ return view.tags.some((tag) => tag.startsWith(CAP_TAG_PREFIX) || tag.startsWith(REQ_TAG_PREFIX));
102
+ }
103
+ /**
104
+ * The fleet's declared use cases, with the model they are drawn over.
105
+ *
106
+ * Reads only, throws nothing, and refuses nothing: every failure is the
107
+ * `unreadable` arm. Callers memoise this per invocation — nothing is cached
108
+ * here, because a module-level cache keyed on a docsDir would leak across the
109
+ * `chdir`-per-test processes AGENTS.md warns about and, in a long-running host,
110
+ * across invocations.
111
+ */
112
+ export async function readUseCases(req) {
113
+ const none = {
114
+ kind: "read",
115
+ views: [],
116
+ model: { elements: [], relationships: [], known: req.known },
117
+ resolve: (id) => id,
118
+ };
119
+ const dir = architectureDir(req.docsDir);
120
+ const documents = await architectureDocuments(dir, [subsystemViewsPath(req.docsDir)]);
121
+ if (!(await mentionsTagPrefix(documents)))
122
+ return none;
123
+ const doc = await loadArchitecture(req.docsDir);
124
+ // loam's standing rule, at project altitude: errors mean no model. A use-case
125
+ // file with one unresolved element leaves the whole project unusable, and
126
+ // grading a flow against half a map is the wrong answer this refuses to give.
127
+ if (doc.errors.length > 0) {
128
+ return { kind: "unreadable", errors: doc.errors.map((e) => e.message) };
129
+ }
130
+ return {
131
+ kind: "read",
132
+ views: (doc.views ?? []).filter(isUseCase),
133
+ model: { elements: doc.elements, relationships: doc.relationships, known: req.known },
134
+ resolve: serviceResolver(doc.elements, req.known),
135
+ };
136
+ }
137
+ //# sourceMappingURL=fleet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet.js","sourceRoot":"","sources":["../../../src/core/usecases/fleet.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The fleet's use cases, read once for whoever asks — and NOT read at all when\n * the fleet demonstrably has none.\n *\n * `validate --all` grades use cases as part of a run that was already loading\n * `architecture/` as a LikeC4 project, so it pays nothing extra. Every reader\n * added in phase 4 is in a different position: `loam diff`, `loam delta`,\n * `loam context` and `loam explore` did not load the fleet map as a project\n * before, and `loam delta` in particular sits in `/loam-implement`'s inner loop\n * — a Langium workspace spin-up there is felt on every iteration of somebody's\n * day. So this module's whole job is to answer \"which use cases does the fleet\n * declare?\" and, in the common case where the answer is none, to answer it\n * without starting LikeC4 at all.\n *\n * THE CHEAP GATE IS A BYTE SCAN, and it is sound rather than heuristic. A view\n * is a use case only if it carries a reserved tag — `#cap-<slug>` or\n * `#req-<slug>`, `isUseCase` below is the whole test — and LikeC4 refuses an\n * undeclared tag, so a fleet with a use case has the literal text `cap-` or\n * `req-` in the file that declares the view AND in the file that declares the\n * tag. A document set in which no file contains either substring therefore\n * declares no use case, and `{ views: [] }` is the correct answer, not an\n * evasion. The scan reads bytes rather than decoded text on purpose: it is\n * looking for an ASCII substring, and `readFile(path, \"utf8\")` on a document\n * that is not UTF-8 would substitute U+FFFD and could hide it.\n *\n * BOTH PREFIXES, because the gate and the predicate answer one question and a\n * gate narrower than the predicate is silence rather than a smaller answer. A\n * fleet whose only flow is tagged `#req-` alone declares `tag req-…` and no\n * `cap-` anywhere, so a `cap-`-only scan returned `false` and none of the five\n * readers ever loaded the project — the widening below would have fixed nothing\n * without this one.\n *\n * The gate FAILS CLOSED. A document this module cannot open is treated as one\n * that might mention the prefix, so the project load happens and reports the\n * failure as a hole. The alternative — reading an EACCES as \"no tag here\" — is\n * the fail-open shape docs/CODE-STYLE.md forbids at a validator, arriving in an\n * optimisation.\n */\nimport { readFile } from \"node:fs/promises\";\nimport type { StepScope } from \"../c4/arch.js\";\nimport { CAP_TAG_PREFIX, REQ_TAG_PREFIX } from \"../capabilities/usecase-join.js\";\nimport type { ParsedView } from \"../c4/parsed/dynamic-views.js\";\nimport { architectureDir, loadArchitecture } from \"../c4/project/architecture.js\";\nimport { architectureDocuments } from \"../c4/project/documents.js\";\nimport { serviceResolver } from \"../c4/resolve/service.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport { subsystemViewsPath } from \"../repo/paths.js\";\n\n/**\n * What the fleet's use cases are, or the honest refusal to say.\n *\n * Two variants rather than a record with an `unreadable` flag, because the\n * fields are not independent: a project that did not parse has no elements to\n * resolve against and no views to grade, and a caller handed\n * `{ views: [], unreadable: true }` is one `.length` check away from printing\n * \"no use cases touch this service\" about a fleet nobody could read. Tagged,\n * that state stops being constructible — every caller has to name the\n * `unreadable` arm to get at the views.\n *\n * `read` with an empty `views` is the ordinary answer for a fleet that draws no\n * use cases, and it is exactly what the cheap gate returns without loading\n * anything: the gate is an optimisation over a question with a known answer,\n * never a third verdict.\n */\nexport type UseCaseScan =\n | {\n kind: \"read\";\n /** The reserved-tag views ONLY — `isUseCase` below is the opt-in, and the only one. */\n views: readonly ParsedView[];\n /**\n * What a hop is attributed against, built ONCE per scan. It carries the\n * fleet set that `resolve` below was built from, which is what stops\n * \"which service is this element\" being answered one way by\n * `attributeStep`'s fallback tier and another by a caller's own join —\n * the same reason `validate`'s `StepGrading` holds both together.\n *\n * `known` is REQUIRED here while `StepScope` leaves it optional, and the\n * narrowing is load-bearing rather than tidy: every scan is built from a\n * `UseCaseRequest` that has one, so a consumer reading `model.known` was\n * writing an `undefined` branch that could not be reached and could not be\n * tested. A `StepScope` is still what `attributeStep` receives.\n */\n model: StepScope & { known: ReadonlySet<string> };\n /** One element→service resolver for the whole scan, built with the enumerated fleet. */\n resolve: (id: string) => string;\n }\n | {\n kind: \"unreadable\";\n /** LikeC4's own messages, so a caller can say WHICH document broke. */\n errors: string[];\n };\n\nexport interface UseCaseRequest {\n docsDir: DocsDir;\n /**\n * The enumerated fleet. It rides into the resolver for the reason every other\n * edge join carries it: without it, a step drawn into a modelled container\n * `payment.api` resolves to a service called \"api\" that has never existed\n * (docs/DESIGN.md's serviceResolver-known row).\n */\n known: ReadonlySet<string>;\n}\n\n/**\n * Does any document in the set even mention a reserved tag prefix?\n *\n * Sequential and short-circuiting ON PURPOSE — the usual \"no await in a loop\"\n * rule is about independent work that all has to finish, and this loop exists\n * precisely so that a fleet whose first file declares a use case stops reading\n * the other ninety-nine.\n *\n * `req-` is the looser of the two substrings and that is accepted knowingly: a\n * document mentioning it in prose costs one project load loam would otherwise\n * have skipped, which is a slower right answer. Dropping it costs a wrong one.\n */\nasync function mentionsTagPrefix(paths: readonly string[]): Promise<boolean> {\n for (const path of paths) {\n try {\n const bytes = await readFile(path);\n if (bytes.includes(CAP_TAG_PREFIX) || bytes.includes(REQ_TAG_PREFIX)) return true;\n } catch {\n // Unreadable is not \"no tag\": the project load below owns that failure and\n // will report it as a hole naming the document. Answering `false` here\n // would let an unopenable file grade the fleet as having no use cases.\n return true;\n }\n }\n return false;\n}\n\n/**\n * Is this a use case at all? — the ONE answer, for `validate` and every reader\n * alike.\n *\n * EITHER RESERVED PREFIX OPTS A VIEW IN. A view carrying `#req-CHK-ONCE` and no\n * `#cap-` tag is an author who asked to be graded and forgot half the claim; it\n * earns `usecase.requirement-unresolved`'s `unscoped` arm rather than silence,\n * and SCHEMA.md has said so since the tag shipped.\n *\n * It is exported because it was the asymmetry, not the rule, that was the\n * defect. `validate --all` widened here and the readers did not, so a\n * `#req-`-only flow was graded hop by hop by the gate and invisible to `loam\n * diff`, `delta`, `status`, the packs and `list capabilities` — a fleet where\n * the gate and the report disagree about what exists, which is worse than\n * either answer alone. One predicate is what stops that recurring: a caller\n * that spells `startsWith` itself is a second answer waiting to drift.\n *\n * The parameter is the smallest shape the test needs rather than `ParsedView`,\n * so a caller holding a view-like row does not have to build one to ask. Both\n * prefix tests match their resolver's exactly, case included, so a view can\n * never be opted in here and read as untagged in `resolveCapabilityTags`.\n *\n * NOT the same question as `flowsClaiming`'s. That one asks which flows claim\n * capability X and matches the whole `cap-<slug>` tag; a `#req-`-only view\n * claims none, and it is correct for it to stay out of that answer while\n * counting as a use case here.\n */\nexport function isUseCase(view: { tags: readonly string[] }): boolean {\n return view.tags.some((tag) => tag.startsWith(CAP_TAG_PREFIX) || tag.startsWith(REQ_TAG_PREFIX));\n}\n\n/**\n * The fleet's declared use cases, with the model they are drawn over.\n *\n * Reads only, throws nothing, and refuses nothing: every failure is the\n * `unreadable` arm. Callers memoise this per invocation — nothing is cached\n * here, because a module-level cache keyed on a docsDir would leak across the\n * `chdir`-per-test processes AGENTS.md warns about and, in a long-running host,\n * across invocations.\n */\nexport async function readUseCases(req: UseCaseRequest): Promise<UseCaseScan> {\n const none: UseCaseScan = {\n kind: \"read\",\n views: [],\n model: { elements: [], relationships: [], known: req.known },\n resolve: (id) => id,\n };\n const dir = architectureDir(req.docsDir);\n const documents = await architectureDocuments(dir, [subsystemViewsPath(req.docsDir)]);\n if (!(await mentionsTagPrefix(documents))) return none;\n\n const doc = await loadArchitecture(req.docsDir);\n // loam's standing rule, at project altitude: errors mean no model. A use-case\n // file with one unresolved element leaves the whole project unusable, and\n // grading a flow against half a map is the wrong answer this refuses to give.\n if (doc.errors.length > 0) {\n return { kind: \"unreadable\", errors: doc.errors.map((e) => e.message) };\n }\n return {\n kind: \"read\",\n views: (doc.views ?? []).filter(isUseCase),\n model: { elements: doc.elements, relationships: doc.relationships, known: req.known },\n resolve: serviceResolver(doc.elements, req.known),\n };\n}\n"]}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Which hops of which business flows a contract removal breaks.
3
+ *
4
+ * `loam diff --base` already answers "who still depends on this operation" with
5
+ * landscape edges and other services' living requirements. Both are true and
6
+ * neither is the sentence a reviewer wants: an edge is a line in a diagram and a
7
+ * requirement is a heading in somebody else's spec, while "step 4 of Checkout
8
+ * stops working" is the fact a human weighs a pull request against. This module
9
+ * is the join that produces that sentence, and it produces NOTHING ELSE — no
10
+ * finding, no code, no severity. `core/diff/victims.ts` folds its answers into
11
+ * the `details[]` of the two codes that already exist.
12
+ *
13
+ * ## Attributed is a victim; contested is a suspension
14
+ *
15
+ * The distinction is the whole reason this module is careful rather than a
16
+ * filter. `attributeStep` (core/c4/arch.ts) grades a hop by the DISTINCT-op
17
+ * count across the relationships backing it:
18
+ *
19
+ * - `attributed` with `op === X` means every relationship behind the hop agrees
20
+ * the hop exercises X. Remove X and the flow breaks at that hop. That is
21
+ * positive evidence, and it is a victim.
22
+ * - `contested` means the relationships DISAGREE, so loam does not know which
23
+ * operation the hop exercises. It must not claim the flow breaks — a guessed
24
+ * victim reads exactly like a real one and turns a warn into an error on
25
+ * somebody's PR — and it must not go silent either, because silence here is
26
+ * the fail-open shape `ConsumerScan.suspended` exists to forbid. It is a
27
+ * suspension: loam says it could not answer.
28
+ *
29
+ * A contested hop is suspended only when X is among its candidate operations.
30
+ * That is not a noise trade, it is the honest boundary: the candidates ARE the
31
+ * relationships backing the hop, so an operation none of them names is an
32
+ * operation the hop cannot be exercising under any reading of the disagreement.
33
+ * Suspending every contested hop in the fleet on every removal would report
34
+ * doubt loam does not actually have.
35
+ *
36
+ * An `unbacked` hop is neither. Nothing in the model backs it, so it names no
37
+ * operation of anybody's contract — `usecase.step-unbacked` is already the error
38
+ * that convicts it, and repeating that here would file a modelling defect
39
+ * against whoever happened to remove an endpoint.
40
+ */
41
+ import { attributeStep } from "../c4/arch.js";
42
+ import { hopPlace } from "./place.js";
43
+ const EMPTY = { breaks: [], unsure: [] };
44
+ /** Every hop of every graded use case, view order preserved, so output is stable. */
45
+ function hops(scan) {
46
+ return scan.views.flatMap((view) => view.steps.map((step) => ({ view, step })));
47
+ }
48
+ /**
49
+ * The flows one removed operation breaks, and the ones loam cannot answer for.
50
+ *
51
+ * An `unreadable` scan contributes NOTHING — neither break nor suspension — and
52
+ * that is `core/diff/victims.ts`'s own landscape doctrine applied one axis over:
53
+ * an unreadable fleet map proves nothing either way, `landscape.invalid` is
54
+ * validate's finding to make, and inventing doubt out of a parse error would
55
+ * point the reviewer at a file their change never touched. The caller reports
56
+ * the hole; this join stays quiet about it.
57
+ */
58
+ export function hopsExercising(scan, removed) {
59
+ if (scan.kind !== "read")
60
+ return EMPTY;
61
+ const out = { breaks: [], unsure: [] };
62
+ for (const { view, step } of hops(scan)) {
63
+ const attribution = attributeStep(step, scan.model);
64
+ if (attribution.verdict === "unbacked")
65
+ continue;
66
+ // The callee, through the SAME resolver every other join in this run uses,
67
+ // so a hop drawn into a modelled container `payment.api` is filed against
68
+ // payment-service rather than against a service called "api".
69
+ if (scan.resolve(attribution.to) !== removed.provider)
70
+ continue;
71
+ if (attribution.verdict === "attributed") {
72
+ if (attribution.op === removed.op)
73
+ out.breaks.push(hopPlace(view, step));
74
+ continue;
75
+ }
76
+ if (!attribution.ops.includes(removed.op))
77
+ continue;
78
+ out.unsure.push(`${hopPlace(view, step)} — ${attribution.rels.length} relationships back ` +
79
+ `${attribution.from} -> ${attribution.to} and they name different operations, so whether this hop ` +
80
+ `exercises '${removed.op}' could not be answered`);
81
+ }
82
+ return out;
83
+ }
84
+ /**
85
+ * The flows one removed message breaks.
86
+ *
87
+ * No suspension arm, and the asymmetry is a fact about the model rather than an
88
+ * omission: `attributeStep` contests OPERATIONS, because that is where two
89
+ * relationships between one pair of endpoints can disagree. A `consumes` edge
90
+ * names a message outright, so a hop either is backed by one or is not — there
91
+ * is no disagreement for loam to refuse to resolve.
92
+ *
93
+ * The provider exclusion mirrors `messageConsumers` exactly: an edge INTO the
94
+ * provider is the provider's own consumption, which leaves with the same change,
95
+ * so a hop backed only by that edge is not a victim of it.
96
+ */
97
+ export function hopsConsuming(scan, removed) {
98
+ if (scan.kind !== "read")
99
+ return [];
100
+ const out = [];
101
+ for (const { view, step } of hops(scan)) {
102
+ const attribution = attributeStep(step, scan.model);
103
+ if (attribution.verdict === "unbacked")
104
+ continue;
105
+ const consumes = attribution.rels.some((rel) => rel.consumes === removed.name && scan.resolve(rel.target) !== removed.provider);
106
+ if (consumes)
107
+ out.push(hopPlace(view, step));
108
+ }
109
+ return out;
110
+ }
111
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../src/core/usecases/operations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which hops of which business flows a contract removal breaks.\n *\n * `loam diff --base` already answers \"who still depends on this operation\" with\n * landscape edges and other services' living requirements. Both are true and\n * neither is the sentence a reviewer wants: an edge is a line in a diagram and a\n * requirement is a heading in somebody else's spec, while \"step 4 of Checkout\n * stops working\" is the fact a human weighs a pull request against. This module\n * is the join that produces that sentence, and it produces NOTHING ELSE — no\n * finding, no code, no severity. `core/diff/victims.ts` folds its answers into\n * the `details[]` of the two codes that already exist.\n *\n * ## Attributed is a victim; contested is a suspension\n *\n * The distinction is the whole reason this module is careful rather than a\n * filter. `attributeStep` (core/c4/arch.ts) grades a hop by the DISTINCT-op\n * count across the relationships backing it:\n *\n * - `attributed` with `op === X` means every relationship behind the hop agrees\n * the hop exercises X. Remove X and the flow breaks at that hop. That is\n * positive evidence, and it is a victim.\n * - `contested` means the relationships DISAGREE, so loam does not know which\n * operation the hop exercises. It must not claim the flow breaks — a guessed\n * victim reads exactly like a real one and turns a warn into an error on\n * somebody's PR — and it must not go silent either, because silence here is\n * the fail-open shape `ConsumerScan.suspended` exists to forbid. It is a\n * suspension: loam says it could not answer.\n *\n * A contested hop is suspended only when X is among its candidate operations.\n * That is not a noise trade, it is the honest boundary: the candidates ARE the\n * relationships backing the hop, so an operation none of them names is an\n * operation the hop cannot be exercising under any reading of the disagreement.\n * Suspending every contested hop in the fleet on every removal would report\n * doubt loam does not actually have.\n *\n * An `unbacked` hop is neither. Nothing in the model backs it, so it names no\n * operation of anybody's contract — `usecase.step-unbacked` is already the error\n * that convicts it, and repeating that here would file a modelling defect\n * against whoever happened to remove an endpoint.\n */\nimport { attributeStep } from \"../c4/arch.js\";\nimport type { ParsedStep, ParsedView } from \"../c4/parsed/dynamic-views.js\";\nimport { hopPlace } from \"./place.js\";\nimport type { UseCaseScan } from \"./fleet.js\";\n\n/**\n * What one contract removal does to the fleet's use cases.\n *\n * Two lists rather than `victims`/`suspended` — the vocabulary\n * `core/diff/victims.ts` uses — because this module answers about FLOWS and that\n * one answers about consumers. The caller does the folding, and naming the two\n * differently is what stops a reader taking this for a second implementation of\n * `ConsumerScan` (it is not: it never scans a document, and it cannot be\n * unreadable — the scan it runs over was already graded).\n */\nexport interface UseCaseHits {\n /** Hops the removal breaks, each spelled by `hopPlace`. */\n breaks: string[];\n /** Hops loam refuses to grade, each saying why in its own sentence. */\n unsure: string[];\n}\n\n/** The one removal a scan is being asked about. */\nexport interface RemovedOperation {\n /** The service dropping it — the same id the diff files its findings under. */\n provider: string;\n /** The operationId leaving the contract. */\n op: string;\n}\n\nconst EMPTY: UseCaseHits = { breaks: [], unsure: [] };\n\n/** Every hop of every graded use case, view order preserved, so output is stable. */\nfunction hops(scan: Extract<UseCaseScan, { kind: \"read\" }>): Array<{ view: ParsedView; step: ParsedStep }> {\n return scan.views.flatMap((view) => view.steps.map((step) => ({ view, step })));\n}\n\n/**\n * The flows one removed operation breaks, and the ones loam cannot answer for.\n *\n * An `unreadable` scan contributes NOTHING — neither break nor suspension — and\n * that is `core/diff/victims.ts`'s own landscape doctrine applied one axis over:\n * an unreadable fleet map proves nothing either way, `landscape.invalid` is\n * validate's finding to make, and inventing doubt out of a parse error would\n * point the reviewer at a file their change never touched. The caller reports\n * the hole; this join stays quiet about it.\n */\nexport function hopsExercising(scan: UseCaseScan, removed: RemovedOperation): UseCaseHits {\n if (scan.kind !== \"read\") return EMPTY;\n const out: UseCaseHits = { breaks: [], unsure: [] };\n for (const { view, step } of hops(scan)) {\n const attribution = attributeStep(step, scan.model);\n if (attribution.verdict === \"unbacked\") continue;\n // The callee, through the SAME resolver every other join in this run uses,\n // so a hop drawn into a modelled container `payment.api` is filed against\n // payment-service rather than against a service called \"api\".\n if (scan.resolve(attribution.to) !== removed.provider) continue;\n if (attribution.verdict === \"attributed\") {\n if (attribution.op === removed.op) out.breaks.push(hopPlace(view, step));\n continue;\n }\n if (!attribution.ops.includes(removed.op)) continue;\n out.unsure.push(\n `${hopPlace(view, step)} — ${attribution.rels.length} relationships back ` +\n `${attribution.from} -> ${attribution.to} and they name different operations, so whether this hop ` +\n `exercises '${removed.op}' could not be answered`,\n );\n }\n return out;\n}\n\n/** The one message removal a scan is being asked about. */\nexport interface RemovedMessage {\n /** The service dropping the declaration — the diff's own subject. */\n provider: string;\n /** The AsyncAPI message name leaving the contract. */\n name: string;\n}\n\n/**\n * The flows one removed message breaks.\n *\n * No suspension arm, and the asymmetry is a fact about the model rather than an\n * omission: `attributeStep` contests OPERATIONS, because that is where two\n * relationships between one pair of endpoints can disagree. A `consumes` edge\n * names a message outright, so a hop either is backed by one or is not — there\n * is no disagreement for loam to refuse to resolve.\n *\n * The provider exclusion mirrors `messageConsumers` exactly: an edge INTO the\n * provider is the provider's own consumption, which leaves with the same change,\n * so a hop backed only by that edge is not a victim of it.\n */\nexport function hopsConsuming(scan: UseCaseScan, removed: RemovedMessage): string[] {\n if (scan.kind !== \"read\") return [];\n const out: string[] = [];\n for (const { view, step } of hops(scan)) {\n const attribution = attributeStep(step, scan.model);\n if (attribution.verdict === \"unbacked\") continue;\n const consumes = attribution.rels.some(\n (rel) => rel.consumes === removed.name && scan.resolve(rel.target) !== removed.provider,\n );\n if (consumes) out.push(hopPlace(view, step));\n }\n return out;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The document the view was written in, spelled repo-relative.
3
+ *
4
+ * `sourcePath` is relative to the LikeC4 PROJECT root, which for
5
+ * `loadArchitecture` is `architecture/`, so the prefix is added here and nowhere
6
+ * else. The caller owes one thing in exchange: only views that came from
7
+ * `loadProject` may be spelled with this, because the single-file loaders name
8
+ * every document after themselves (`source.c4`) and this function would then
9
+ * spell a path that has never existed.
10
+ *
11
+ * An ABSENT `sourcePath` names the directory rather than guessing a file.
12
+ * LikeC4 synthesizes only element views and `readDynamicViews` drops those, so
13
+ * nothing at the 1.59.2 pin reaches this arm; if a release ever stopped emitting
14
+ * the field, falling back to `architecture/landscape.likec4` would put a false
15
+ * cross-reference in a message.
16
+ */
17
+ export function viewFile(view) {
18
+ return view.sourcePath === undefined ? "architecture/" : `architecture/${view.sourcePath}`;
19
+ }
20
+ /**
21
+ * `step 4 'confirms the order'`, or `step 4` where the author labelled nothing.
22
+ *
23
+ * The ordinal is the view's own leaf numbering (`ParsedStep.ordinal`), which
24
+ * skips group brackets — so it counts what the author sees on the diagram, and
25
+ * a message naming step 4 is naming the fourth arrow.
26
+ */
27
+ export function stepPlace(step) {
28
+ return step.title === undefined ? `step ${step.ordinal}` : `step ${step.ordinal} '${step.title}'`;
29
+ }
30
+ /**
31
+ * A use case and one of its hops, named the way a reader outside `validate`
32
+ * meets them: `use case 'uc_checkout' step 4 'authorizes the payment'
33
+ * (architecture/usecases/checkout.likec4)`.
34
+ *
35
+ * The file goes LAST here and first in validate's `viewPlace`, and that is not
36
+ * an inconsistency to tidy away. A validate finding is a line in a report about
37
+ * documents, so it leads with the document; a diff victim is one entry in a
38
+ * `details[]` list whose other entries name edges and requirements, so it has to
39
+ * lead with the thing that broke and carry the file as the place to go fix it.
40
+ */
41
+ export function hopPlace(view, step) {
42
+ return `use case '${view.id}' ${stepPlace(step)} (${viewFile(view)})`;
43
+ }
44
+ //# sourceMappingURL=place.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"place.js","sourceRoot":"","sources":["../../../src/core/usecases/place.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAgB;IACvC,OAAO,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,UAAU,EAAE,CAAC;AAC7F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAgB;IACxC,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,GAAG,CAAC;AACpG,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAgB,EAAE,IAAgB;IACzD,OAAO,aAAa,IAAI,CAAC,EAAE,KAAK,SAAS,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;AACxE,CAAC","sourcesContent":["/**\n * Where a use case lives and where one of its hops sits — the two spellings\n * every surface that mentions a use case has to agree on.\n *\n * They began in `commands/validate/fleet/usecases/place.ts`, beside the four\n * `usecase.*` graders that were their only reader. `loam diff` naming the flow a\n * removal breaks is the second reader and `loam delta`/`loam context` are the\n * third and fourth, and `core/` may not import from `commands/` (AGENTS.md), so\n * the alternative was a second spelling of `architecture/<sourcePath>` in core.\n * The two would drift silently and in the worst possible direction: a file path\n * in a message that no longer names the file the view is in is how the next\n * reader stops looking (docs/CODE-STYLE.md).\n *\n * What did NOT move is `viewPlace`, the `landscape: …` opening the validate\n * findings carry. That prefix is the name of a validate TARGET, so it is command\n * vocabulary — a `loam diff` finding is filed against a service, not against the\n * fleet map, and borrowing validate's prefix there would claim a target that run\n * does not have.\n */\nimport type { ParsedStep, ParsedView } from \"../c4/parsed/dynamic-views.js\";\n\n/**\n * The document the view was written in, spelled repo-relative.\n *\n * `sourcePath` is relative to the LikeC4 PROJECT root, which for\n * `loadArchitecture` is `architecture/`, so the prefix is added here and nowhere\n * else. The caller owes one thing in exchange: only views that came from\n * `loadProject` may be spelled with this, because the single-file loaders name\n * every document after themselves (`source.c4`) and this function would then\n * spell a path that has never existed.\n *\n * An ABSENT `sourcePath` names the directory rather than guessing a file.\n * LikeC4 synthesizes only element views and `readDynamicViews` drops those, so\n * nothing at the 1.59.2 pin reaches this arm; if a release ever stopped emitting\n * the field, falling back to `architecture/landscape.likec4` would put a false\n * cross-reference in a message.\n */\nexport function viewFile(view: ParsedView): string {\n return view.sourcePath === undefined ? \"architecture/\" : `architecture/${view.sourcePath}`;\n}\n\n/**\n * `step 4 'confirms the order'`, or `step 4` where the author labelled nothing.\n *\n * The ordinal is the view's own leaf numbering (`ParsedStep.ordinal`), which\n * skips group brackets — so it counts what the author sees on the diagram, and\n * a message naming step 4 is naming the fourth arrow.\n */\nexport function stepPlace(step: ParsedStep): string {\n return step.title === undefined ? `step ${step.ordinal}` : `step ${step.ordinal} '${step.title}'`;\n}\n\n/**\n * A use case and one of its hops, named the way a reader outside `validate`\n * meets them: `use case 'uc_checkout' step 4 'authorizes the payment'\n * (architecture/usecases/checkout.likec4)`.\n *\n * The file goes LAST here and first in validate's `viewPlace`, and that is not\n * an inconsistency to tidy away. A validate finding is a line in a report about\n * documents, so it leads with the document; a diff victim is one entry in a\n * `details[]` list whose other entries name edges and requirements, so it has to\n * lead with the thing that broke and carry the file as the place to go fix it.\n */\nexport function hopPlace(view: ParsedView, step: ParsedStep): string {\n return `use case '${view.id}' ${stepPlace(step)} (${viewFile(view)})`;\n}\n"]}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Which business flows run through a service — the blast radius `loam delta`
3
+ * and `loam status` report beside the requirements and contracts a feature
4
+ * changes.
5
+ *
6
+ * ## The join is on ENDPOINTS, never on relationships
7
+ *
8
+ * `core/usecases/operations.ts` asks what a hop EXERCISES, which needs the
9
+ * relationship behind it. This asks something weaker and more useful early:
10
+ * does this hop mention the service at all? So it resolves the step's own two
11
+ * endpoints and stops — no `attributeStep`, no candidate scan.
12
+ *
13
+ * That is the difference between answering and not answering for the service a
14
+ * feature is about to INTRODUCE. A flow drawn for work that has not happened yet
15
+ * has hops nothing in the model backs — `usecase.step-unbacked` is validate's
16
+ * finding about exactly that, and it is an ERROR precisely because the fleet map
17
+ * has not caught up — and a relationship join reports such a flow as touching
18
+ * nobody. The endpoints are still there; the arrow is what is missing. An
19
+ * implementer reading a delta brief needs to know the flow exists most of all in
20
+ * that state, because that is the state their change is supposed to end.
21
+ *
22
+ * ## Everything a caller sees, it sees per HOP
23
+ *
24
+ * A view is reported only when at least one of its hops touches the addressed
25
+ * services, and only those hops travel. "Checkout touches you" is not actionable;
26
+ * "Checkout touches you at step 4" is where the reader goes to look.
27
+ */
28
+ import { compareIds } from "../repo/entries.js";
29
+ import { readUseCases } from "./fleet.js";
30
+ import { viewFile } from "./place.js";
31
+ /** The addressed services one hop mentions, source and target both, sorted and de-duplicated. */
32
+ function touched(step, scan, services) {
33
+ const hit = [step.source, step.target].map((id) => scan.resolve(id)).filter((id) => services.has(id));
34
+ return [...new Set(hit)].sort(compareIds);
35
+ }
36
+ /** One view's touching hops, in the author's own step order — the order the diagram reads in. */
37
+ function touchedView(view, scan, services) {
38
+ const steps = [];
39
+ for (const step of view.steps) {
40
+ const hit = touched(step, scan, services);
41
+ if (hit.length === 0)
42
+ continue;
43
+ steps.push({
44
+ ordinal: step.ordinal,
45
+ ...(step.title === undefined ? {} : { title: step.title }),
46
+ source: step.source,
47
+ target: step.target,
48
+ services: hit,
49
+ });
50
+ }
51
+ if (steps.length === 0)
52
+ return null;
53
+ return {
54
+ id: view.id,
55
+ ...(view.title === undefined ? {} : { title: view.title }),
56
+ file: viewFile(view),
57
+ tags: [...view.tags].sort(compareIds),
58
+ steps,
59
+ };
60
+ }
61
+ /**
62
+ * The flows touching a set of services, over a scan somebody already has.
63
+ *
64
+ * Separate from `useCaseBlastRadius` below because `core/pack/joins.ts` reads
65
+ * the scan ONCE and asks two different questions of it — this one and the
66
+ * capability claim join — and a second read there would be a second LikeC4
67
+ * project load inside a single `loam context`.
68
+ *
69
+ * An `unreadable` scan answers with nothing. Every caller pairs this with the
70
+ * scan's own health, because an empty list means "no flow mentions these
71
+ * services" and "nobody could look" only when read together.
72
+ */
73
+ export function flowsTouching(scan, services) {
74
+ if (scan.kind !== "read")
75
+ return [];
76
+ return (scan.views
77
+ .map((view) => touchedView(view, scan, services))
78
+ .filter((flow) => flow !== null)
79
+ // Sorted by (file, view id) rather than reported in LikeC4's own record
80
+ // order: nothing in loam has measured that the parse preserves declaration
81
+ // order, so a payload ordered by it would reorder under a dependency bump.
82
+ .sort((a, b) => compareIds(a.file, b.file) || compareIds(a.id, b.id)));
83
+ }
84
+ /**
85
+ * The flows touching a set of services, read and joined in one call.
86
+ *
87
+ * The load underneath is `readUseCases`, which carries its own cheap gate: a
88
+ * fleet whose `architecture/` documents never mention the reserved tag prefix
89
+ * answers `{ flows: [] }` without starting LikeC4 at all. That gate is what
90
+ * makes this callable from `loam delta`, which sits in `/loam-implement`'s inner
91
+ * loop and would otherwise pay a Langium workspace spin-up on every iteration of
92
+ * somebody's afternoon.
93
+ */
94
+ export async function useCaseBlastRadius(req) {
95
+ const scan = await readUseCases({ docsDir: req.docsDir, known: req.known });
96
+ if (scan.kind !== "read") {
97
+ return { unreadable: true, ...(scan.errors[0] === undefined ? {} : { error: scan.errors[0] }), flows: [] };
98
+ }
99
+ return { unreadable: false, flows: flowsTouching(scan, req.services) };
100
+ }
101
+ //# sourceMappingURL=touch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"touch.js","sourceRoot":"","sources":["../../../src/core/usecases/touch.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which business flows run through a service — the blast radius `loam delta`\n * and `loam status` report beside the requirements and contracts a feature\n * changes.\n *\n * ## The join is on ENDPOINTS, never on relationships\n *\n * `core/usecases/operations.ts` asks what a hop EXERCISES, which needs the\n * relationship behind it. This asks something weaker and more useful early:\n * does this hop mention the service at all? So it resolves the step's own two\n * endpoints and stops — no `attributeStep`, no candidate scan.\n *\n * That is the difference between answering and not answering for the service a\n * feature is about to INTRODUCE. A flow drawn for work that has not happened yet\n * has hops nothing in the model backs — `usecase.step-unbacked` is validate's\n * finding about exactly that, and it is an ERROR precisely because the fleet map\n * has not caught up — and a relationship join reports such a flow as touching\n * nobody. The endpoints are still there; the arrow is what is missing. An\n * implementer reading a delta brief needs to know the flow exists most of all in\n * that state, because that is the state their change is supposed to end.\n *\n * ## Everything a caller sees, it sees per HOP\n *\n * A view is reported only when at least one of its hops touches the addressed\n * services, and only those hops travel. \"Checkout touches you\" is not actionable;\n * \"Checkout touches you at step 4\" is where the reader goes to look.\n */\nimport { compareIds } from \"../repo/entries.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport type { ParsedStep, ParsedView } from \"../c4/parsed/dynamic-views.js\";\nimport { readUseCases, type UseCaseScan } from \"./fleet.js\";\nimport { viewFile } from \"./place.js\";\n\n/** One hop of a flow that mentions a service the caller asked about. */\nexport interface TouchedStep {\n /** The view's own leaf numbering — what the author sees on the diagram. */\n ordinal: number;\n title?: string;\n /** The step's endpoints as the view declares them: element ids, already oriented. */\n source: string;\n target: string;\n /**\n * Which of the ADDRESSED services this hop mentions, sorted. Both, when a flow\n * calls a service and it answers within the same addressed set.\n */\n services: string[];\n}\n\n/** One flow that touches the addressed services, with only the hops that do. */\nexport interface TouchedUseCase {\n id: string;\n title?: string;\n /** Repo-relative document the view is written in — where the reader goes to change it. */\n file: string;\n /**\n * The `#cap-` tags the view carries, verbatim and unresolved.\n *\n * Verbatim because resolving them means reading\n * `architecture/capabilities.yaml`, and a slug is not injective — the join can\n * legitimately answer \"two capabilities\" (core/capabilities/usecase-join.ts).\n * A blast-radius list is not the place to make that call: `loam validate --all`\n * already grades every tag with `usecase.capability-unresolved`, and a second\n * resolution here would be a second answer to the same question.\n */\n tags: string[];\n steps: TouchedStep[];\n}\n\n/**\n * The flows over a set of services, and whether loam could look at all.\n *\n * `unreadable` is a HOLE, in the sense `core/pack/pack.ts` uses the word: `flows`\n * is empty because `architecture/` did not parse, not because the fleet draws no\n * use cases. A consumer that cannot tell those apart reads \"no business flow\n * depends on this\" off a directory nobody could open, which is the vacuously\n * green answer every surface in loam is built to refuse.\n */\nexport interface UseCaseBlastRadius {\n unreadable: boolean;\n /** LikeC4's first message, so a reader knows which document to open. */\n error?: string;\n flows: TouchedUseCase[];\n}\n\nexport interface BlastRadiusRequest {\n docsDir: DocsDir;\n /**\n * The fleet the element→service resolver is built with. Wider than `services`\n * on purpose: it should be every service the resolver could legitimately land\n * on, so a hop into a modelled container resolves to its owner rather than to\n * the container's own title.\n */\n known: ReadonlySet<string>;\n /** The services being asked about. */\n services: ReadonlySet<string>;\n}\n\n/** The addressed services one hop mentions, source and target both, sorted and de-duplicated. */\nfunction touched(step: ParsedStep, scan: Extract<UseCaseScan, { kind: \"read\" }>, services: ReadonlySet<string>): string[] {\n const hit = [step.source, step.target].map((id) => scan.resolve(id)).filter((id) => services.has(id));\n return [...new Set(hit)].sort(compareIds);\n}\n\n/** One view's touching hops, in the author's own step order — the order the diagram reads in. */\nfunction touchedView(view: ParsedView, scan: Extract<UseCaseScan, { kind: \"read\" }>, services: ReadonlySet<string>): TouchedUseCase | null {\n const steps: TouchedStep[] = [];\n for (const step of view.steps) {\n const hit = touched(step, scan, services);\n if (hit.length === 0) continue;\n steps.push({\n ordinal: step.ordinal,\n ...(step.title === undefined ? {} : { title: step.title }),\n source: step.source,\n target: step.target,\n services: hit,\n });\n }\n if (steps.length === 0) return null;\n return {\n id: view.id,\n ...(view.title === undefined ? {} : { title: view.title }),\n file: viewFile(view),\n tags: [...view.tags].sort(compareIds),\n steps,\n };\n}\n\n/**\n * The flows touching a set of services, over a scan somebody already has.\n *\n * Separate from `useCaseBlastRadius` below because `core/pack/joins.ts` reads\n * the scan ONCE and asks two different questions of it — this one and the\n * capability claim join — and a second read there would be a second LikeC4\n * project load inside a single `loam context`.\n *\n * An `unreadable` scan answers with nothing. Every caller pairs this with the\n * scan's own health, because an empty list means \"no flow mentions these\n * services\" and \"nobody could look\" only when read together.\n */\nexport function flowsTouching(scan: UseCaseScan, services: ReadonlySet<string>): TouchedUseCase[] {\n if (scan.kind !== \"read\") return [];\n return (\n scan.views\n .map((view) => touchedView(view, scan, services))\n .filter((flow): flow is TouchedUseCase => flow !== null)\n // Sorted by (file, view id) rather than reported in LikeC4's own record\n // order: nothing in loam has measured that the parse preserves declaration\n // order, so a payload ordered by it would reorder under a dependency bump.\n .sort((a, b) => compareIds(a.file, b.file) || compareIds(a.id, b.id))\n );\n}\n\n/**\n * The flows touching a set of services, read and joined in one call.\n *\n * The load underneath is `readUseCases`, which carries its own cheap gate: a\n * fleet whose `architecture/` documents never mention the reserved tag prefix\n * answers `{ flows: [] }` without starting LikeC4 at all. That gate is what\n * makes this callable from `loam delta`, which sits in `/loam-implement`'s inner\n * loop and would otherwise pay a Langium workspace spin-up on every iteration of\n * somebody's afternoon.\n */\nexport async function useCaseBlastRadius(req: BlastRadiusRequest): Promise<UseCaseBlastRadius> {\n const scan = await readUseCases({ docsDir: req.docsDir, known: req.known });\n if (scan.kind !== \"read\") {\n return { unreadable: true, ...(scan.errors[0] === undefined ? {} : { error: scan.errors[0] }), flows: [] };\n }\n return { unreadable: false, flows: flowsTouching(scan, req.services) };\n}\n"]}