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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,116 @@
1
+ import { withFeatureCapabilities } from "../../capabilities/delta/overlay.js";
2
+ import { gradableCapabilityIds } from "../../capabilities/findings.js";
3
+ import { resolveCapabilityTags, resolveRequirementTags } from "../../capabilities/usecase-join.js";
4
+ import { compareIds } from "../../repo/entries.js";
5
+ import { readFeatureFlows } from "./overlay.js";
6
+ /**
7
+ * The promises this feature's own flows keep, read over the post-merge map.
8
+ *
9
+ * A feature with no flow is `read` with nothing kept, which is the ordinary
10
+ * answer and costs one walk over a directory that is not there.
11
+ */
12
+ export async function featureFlowPromises(req) {
13
+ const scan = await readFeatureFlows(req);
14
+ if (scan.kind === "unreadable")
15
+ return { kind: "unreadable", errors: scan.errors };
16
+ // The ladder, and it is `ungraded` rather than an empty answer. Once the
17
+ // feature's own capability deltas are unioned in, `declared` is null for
18
+ // exactly one reason — `architecture/capabilities.yaml` exists and does not
19
+ // read as a vocabulary — so no `#cap-` tag can resolve and no claim can be
20
+ // made either way. Returning "keeps nothing" here is what let
21
+ // `capability.uncovered` convict a flow that does resolve, over a file the
22
+ // feature gate reports nothing else about.
23
+ if (req.declared === null) {
24
+ return {
25
+ kind: "ungraded",
26
+ why: "architecture/capabilities.yaml does not read as a capability vocabulary (`loam validate --all` reports it as capability.invalid)",
27
+ };
28
+ }
29
+ // A real, empty answer: loam read the flows and none of them claims anything.
30
+ if (scan.views.length === 0)
31
+ return { kind: "read", kept: [] };
32
+ const byPromise = new Map();
33
+ for (const view of scan.views) {
34
+ const scope = resolveCapabilityTags(view.tags, req.declared).flatMap((claim) => claim.kind === "resolved" ? [claim.id] : []);
35
+ for (const claim of resolveRequirementTags(view.tags, scope, req.requirementsOf)) {
36
+ if (claim.kind !== "resolved")
37
+ continue;
38
+ // Keyed on the pair, never on a joined string: see the header. The Map is
39
+ // only here to collapse two views keeping one promise into one row.
40
+ const key = JSON.stringify([claim.capability, claim.id]);
41
+ const found = byPromise.get(key);
42
+ if (found === undefined) {
43
+ byPromise.set(key, {
44
+ promise: { capability: claim.capability, requirement: claim.id, flows: [] },
45
+ flows: new Set([view.id]),
46
+ });
47
+ }
48
+ else {
49
+ found.flows.add(view.id);
50
+ }
51
+ }
52
+ }
53
+ // Sorted, and the view ids inside each row sorted too: nothing in loam has
54
+ // measured that LikeC4 preserves view declaration order, so a message built
55
+ // from it would reorder under a dependency bump.
56
+ const kept = [...byPromise.values()]
57
+ .map(({ promise, flows }) => ({ ...promise, flows: [...flows].sort(compareIds) }))
58
+ .sort((a, b) => compareIds(a.capability, b.capability) || compareIds(a.requirement, b.requirement));
59
+ return { kind: "read", kept };
60
+ }
61
+ /**
62
+ * What this feature's own flows keep, resolved against the vocabulary and the
63
+ * requirement ids its own merge would leave behind.
64
+ *
65
+ * THE WIDENING IS THE POINT, and it is `capabilities/delta/overlay.ts`'s
66
+ * judgement applied to the other carrier: an analyst adds `NOTIFY-ONCE` to a
67
+ * capability delta and an architect answers it with a flow in the same change,
68
+ * so a `#req-` tag resolved against the LIVING tree alone names a promise that
69
+ * does not exist yet — and the same archive that would land the flow lands the
70
+ * promise it points at.
71
+ *
72
+ * `requirementsOf` KNOWS ONLY THE CAPABILITIES THIS FEATURE HAS A DELTA FOR, and
73
+ * within those it carries living ∪ ADDED ∪ MODIFIED ids. The narrowing changes
74
+ * no answer, for a reason stronger than "it is probably fine":
75
+ * `uncoveredIssues` grades `r.kind === "ADDED"` and nothing else, and every
76
+ * ADDED requirement it grades lives in one of these documents by construction —
77
+ * so no id outside this map can reach a verdict. The LIVING half is in there
78
+ * because a widening of the graded set must not silently need a second edit
79
+ * here; today it decides nothing. A flow keeping a promise of a capability this
80
+ * feature does NOT touch resolves to `undocumented` and counts for nothing,
81
+ * which is correct: the fleet-scope `capability.requirement-unrealized` grades
82
+ * that join over the whole corpus, and answering it here from a partial index is
83
+ * how two surfaces start disagreeing.
84
+ *
85
+ * `declared` IS NOT NARROWED, and that asymmetry is load-bearing. It is what
86
+ * `resolveCapabilityTags` compares a `#cap-` slug against, and a slug is not
87
+ * injective — `payments/refunds` and `payments-refunds` flatten to one. Handing
88
+ * it a short list would resolve a tag that is genuinely ambiguous fleet-wide and
89
+ * attribute a whole flow to the wrong capability, silently. So the vocabulary is
90
+ * the fleet's whole one, read once by the caller.
91
+ */
92
+ export async function flowCoverage(req) {
93
+ const vocab = withFeatureCapabilities(req.vocabulary, req.capabilities.map((doc) => doc.id));
94
+ const byCapability = new Map();
95
+ for (const doc of req.capabilities) {
96
+ const ids = new Set();
97
+ // REMOVED is excluded on both sides — a promise on its way out keeps nothing.
98
+ for (const r of doc.living)
99
+ if (r.kind !== "REMOVED" && r.id !== undefined)
100
+ ids.add(r.id);
101
+ for (const r of doc.reqs)
102
+ if ((r.kind === "ADDED" || r.kind === "MODIFIED") && r.id !== undefined)
103
+ ids.add(r.id);
104
+ byCapability.set(doc.id, ids);
105
+ }
106
+ return featureFlowPromises({
107
+ docsDir: req.docsDir,
108
+ featureDir: req.featureDir,
109
+ featureId: req.featureId,
110
+ known: req.known,
111
+ declared: gradableCapabilityIds(vocab),
112
+ requirementsOf: (capability) => byCapability.get(capability),
113
+ ...(req.load === undefined ? {} : { load: req.load }),
114
+ });
115
+ }
116
+ //# sourceMappingURL=claims.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims.js","sourceRoot":"","sources":["../../../../src/core/usecases/delta/claims.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which of this feature's own promises its own flows keep.\n *\n * A cross-service criterion — \"I enter a login and a password and I am in\" —\n * belongs to no single service's spec, because each service promises only its\n * own part. A `dynamic view` can carry it, because it IS the hop sequence, and\n * that is the whole reason `#req-` exists. Until `./flows.ts` gave a feature a\n * slot for one, this answer could only ever be empty inside a feature window:\n * the flow was a fleet document, so a tag naming a requirement the same change\n * was adding named something that was not living yet.\n *\n * WHAT THIS FEEDS. `capability.uncovered` — the archive gate that refuses a\n * promise nothing keeps. Its message used to end by telling authors to archive\n * with `--approve` and tag the flow AFTERWARDS, which was honest advice about a\n * hole rather than a workflow anybody wanted: it lands the promise first, kept\n * by nothing, and relies on somebody coming back.\n *\n * THE RULE IS `../capability.ts`'s AND MUST STAY SO. Only a RESOLVED claim\n * counts, and both halves must resolve — the `#cap-` tag to exactly one declared\n * capability, the `#req-` tag to exactly one of its requirements. A broken tag\n * of either kind is already an error (`usecase.capability-unresolved`,\n * `usecase.requirement-unresolved`) and must never also mark a promise kept: a\n * typo that silenced the gate would turn a mistake into a green archive. The two\n * resolvers are called directly rather than through\n * `useCaseRequirementClaims` for one reason, and it is not a difference of\n * opinion about the rule — that function flattens its answer to a `<cap>#<id>`\n * string, and `capabilities/delta/uncovered.ts` keys this same join on NUL\n * precisely because a capability id has no grammar forbidding `#`. Handing it a\n * key it has written down a reason not to use would be the wrong kind of reuse.\n *\n * THE LADDER TRAVELS. `declared === null` — an absent or unreadable\n * `architecture/capabilities.yaml` — is an empty answer, not a suspended one, and\n * the caller is already suppressing the whole family behind it. An `unreadable`\n * overlay is different and must NOT be flattened to \"keeps nothing\": loam did not\n * look, which is never the same answer as \"there is nothing there\", and a gate\n * that refused on it would blame the author for a map it could not read.\n */\nimport type { LoadedDoc } from \"../../c4/likec4.js\";\nimport type { CapabilityVocabulary } from \"../../capabilities/capabilities.js\";\nimport { withFeatureCapabilities } from \"../../capabilities/delta/overlay.js\";\nimport type { CapabilityDeltaDoc } from \"../../capabilities/delta/uncovered.js\";\nimport { gradableCapabilityIds } from \"../../capabilities/findings.js\";\nimport { resolveCapabilityTags, resolveRequirementTags } from \"../../capabilities/usecase-join.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport { compareIds } from \"../../repo/entries.js\";\nimport { readFeatureFlows, type FlowOverlayRequest } from \"./overlay.js\";\n\n/** One promise this feature's flows keep, with the flows that keep it. */\nexport interface FlowPromise {\n /** The capability the keeping view's `#cap-` tag resolved to. */\n capability: string;\n /** The `Requirement-ID` its `#req-` tag resolved to, inside that capability. */\n requirement: string;\n /** The view ids keeping it, deduplicated and sorted — message text, never identity. */\n flows: readonly string[];\n}\n\n/**\n * What the feature's flows keep, or one of the two honest refusals to say.\n *\n * THREE ARMS, because the two failures are not the same failure and a caller\n * that merged them would say the wrong thing. `unreadable` is loam unable to\n * READ the flows — a parse error, a hop naming an element the merge does not\n * land — and it earns `usecase.flow-invalid` and a refusal. `ungraded` is loam\n * able to read them and unable to RESOLVE their tags, because the capability\n * vocabulary this feature's merge would leave behind does not parse; the flows\n * are fine, the file they point into is not, and refusing over the flows would\n * name the wrong document.\n *\n * `read` with an empty `kept` is the ordinary answer and a real one: loam\n * looked, and no flow of this feature keeps a promise it adds.\n */\nexport type FlowPromises =\n | { kind: \"read\"; kept: readonly FlowPromise[] }\n | { kind: \"ungraded\"; why: string }\n | { kind: \"unreadable\"; errors: string[] };\n\n/** Everything the join needs, on top of what the overlay already asks for. */\nexport interface FlowPromisesRequest extends FlowOverlayRequest {\n /**\n * The capability ids to resolve `#cap-` against — the LIVING vocabulary\n * widened with the ids this feature's own capability deltas introduce, which\n * is the same both-corpora rule `Realizes:` follows\n * (`core/capabilities/delta/overlay.ts`). `null` suspends the axis.\n */\n declared: readonly string[] | null;\n /**\n * The requirement ids per capability, widened the same way\n * (`withFeatureRequirements`). `undefined` for a capability with no document\n * at all is a different answer from an empty set, and both are real here.\n */\n requirementsOf: (capability: string) => ReadonlySet<string> | undefined;\n}\n\n/**\n * The promises this feature's own flows keep, read over the post-merge map.\n *\n * A feature with no flow is `read` with nothing kept, which is the ordinary\n * answer and costs one walk over a directory that is not there.\n */\nexport async function featureFlowPromises(req: FlowPromisesRequest): Promise<FlowPromises> {\n const scan = await readFeatureFlows(req);\n if (scan.kind === \"unreadable\") return { kind: \"unreadable\", errors: scan.errors };\n // The ladder, and it is `ungraded` rather than an empty answer. Once the\n // feature's own capability deltas are unioned in, `declared` is null for\n // exactly one reason — `architecture/capabilities.yaml` exists and does not\n // read as a vocabulary — so no `#cap-` tag can resolve and no claim can be\n // made either way. Returning \"keeps nothing\" here is what let\n // `capability.uncovered` convict a flow that does resolve, over a file the\n // feature gate reports nothing else about.\n if (req.declared === null) {\n return {\n kind: \"ungraded\",\n why: \"architecture/capabilities.yaml does not read as a capability vocabulary (`loam validate --all` reports it as capability.invalid)\",\n };\n }\n // A real, empty answer: loam read the flows and none of them claims anything.\n if (scan.views.length === 0) return { kind: \"read\", kept: [] };\n\n const byPromise = new Map<string, { promise: FlowPromise; flows: Set<string> }>();\n for (const view of scan.views) {\n const scope = resolveCapabilityTags(view.tags, req.declared).flatMap((claim) =>\n claim.kind === \"resolved\" ? [claim.id] : [],\n );\n for (const claim of resolveRequirementTags(view.tags, scope, req.requirementsOf)) {\n if (claim.kind !== \"resolved\") continue;\n // Keyed on the pair, never on a joined string: see the header. The Map is\n // only here to collapse two views keeping one promise into one row.\n const key = JSON.stringify([claim.capability, claim.id]);\n const found = byPromise.get(key);\n if (found === undefined) {\n byPromise.set(key, {\n promise: { capability: claim.capability, requirement: claim.id, flows: [] },\n flows: new Set([view.id]),\n });\n } else {\n found.flows.add(view.id);\n }\n }\n }\n // Sorted, and the view ids inside each row sorted too: nothing in loam has\n // measured that LikeC4 preserves view declaration order, so a message built\n // from it would reorder under a dependency bump.\n const kept = [...byPromise.values()]\n .map(({ promise, flows }) => ({ ...promise, flows: [...flows].sort(compareIds) }))\n .sort((a, b) => compareIds(a.capability, b.capability) || compareIds(a.requirement, b.requirement));\n return { kind: \"read\", kept };\n}\n\n/** Everything the coverage join needs, and nothing it would have to read twice. */\nexport interface FlowCoverageRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n /**\n * The feature's capability deltas, ALREADY PARSED by the caller's walk. They\n * arrive rather than being read because the one caller has just produced them,\n * and re-reading a document to answer a question about it is how two answers\n * about one file appear.\n */\n capabilities: readonly CapabilityDeltaDoc[];\n /** The LIVING capability vocabulary, read by the caller through its own index. */\n vocabulary: CapabilityVocabulary;\n /** The enumerated fleet, for `readFeatureFlows`' resolver and for its reason. */\n known: ReadonlySet<string>;\n /** The caller's memoised LikeC4 read; see `./overlay.ts`. */\n load?: (path: string) => Promise<LoadedDoc>;\n}\n\n/**\n * What this feature's own flows keep, resolved against the vocabulary and the\n * requirement ids its own merge would leave behind.\n *\n * THE WIDENING IS THE POINT, and it is `capabilities/delta/overlay.ts`'s\n * judgement applied to the other carrier: an analyst adds `NOTIFY-ONCE` to a\n * capability delta and an architect answers it with a flow in the same change,\n * so a `#req-` tag resolved against the LIVING tree alone names a promise that\n * does not exist yet — and the same archive that would land the flow lands the\n * promise it points at.\n *\n * `requirementsOf` KNOWS ONLY THE CAPABILITIES THIS FEATURE HAS A DELTA FOR, and\n * within those it carries living ∪ ADDED ∪ MODIFIED ids. The narrowing changes\n * no answer, for a reason stronger than \"it is probably fine\":\n * `uncoveredIssues` grades `r.kind === \"ADDED\"` and nothing else, and every\n * ADDED requirement it grades lives in one of these documents by construction —\n * so no id outside this map can reach a verdict. The LIVING half is in there\n * because a widening of the graded set must not silently need a second edit\n * here; today it decides nothing. A flow keeping a promise of a capability this\n * feature does NOT touch resolves to `undocumented` and counts for nothing,\n * which is correct: the fleet-scope `capability.requirement-unrealized` grades\n * that join over the whole corpus, and answering it here from a partial index is\n * how two surfaces start disagreeing.\n *\n * `declared` IS NOT NARROWED, and that asymmetry is load-bearing. It is what\n * `resolveCapabilityTags` compares a `#cap-` slug against, and a slug is not\n * injective — `payments/refunds` and `payments-refunds` flatten to one. Handing\n * it a short list would resolve a tag that is genuinely ambiguous fleet-wide and\n * attribute a whole flow to the wrong capability, silently. So the vocabulary is\n * the fleet's whole one, read once by the caller.\n */\nexport async function flowCoverage(req: FlowCoverageRequest): Promise<FlowPromises> {\n const vocab = withFeatureCapabilities(req.vocabulary, req.capabilities.map((doc) => doc.id));\n const byCapability = new Map<string, ReadonlySet<string>>();\n for (const doc of req.capabilities) {\n const ids = new Set<string>();\n // REMOVED is excluded on both sides — a promise on its way out keeps nothing.\n for (const r of doc.living) if (r.kind !== \"REMOVED\" && r.id !== undefined) ids.add(r.id);\n for (const r of doc.reqs) if ((r.kind === \"ADDED\" || r.kind === \"MODIFIED\") && r.id !== undefined) ids.add(r.id);\n byCapability.set(doc.id, ids);\n }\n return featureFlowPromises({\n docsDir: req.docsDir,\n featureDir: req.featureDir,\n featureId: req.featureId,\n known: req.known,\n declared: gradableCapabilityIds(vocab),\n requirementsOf: (capability) => byCapability.get(capability),\n ...(req.load === undefined ? {} : { load: req.load }),\n });\n}\n"]}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * A feature's own flows: `features/<FEAT>/usecases/<name>.likec4`.
3
+ *
4
+ * A `dynamic view` is the only realizer a cross-service promise has, and until
5
+ * this slot existed it was the one realizer no feature could carry. Every other
6
+ * axis had a delta — a C4 delta, a requirement delta on both spec axes, both
7
+ * contract deltas, a capability delta, a glossary term — so the business axis's
8
+ * own headline case had to be written backwards: an analyst adds a capability
9
+ * requirement, the architect answers it with a flow, and the flow could not ship
10
+ * in that change. It merged only after the promise was already living, which
11
+ * meant the promise landed first with nothing keeping it.
12
+ *
13
+ * CREATE-ONLY, for `core/glossary/delta.ts`'s reasons rather than a weaker
14
+ * version of them. A capability document is a DELTA because it merges INTO a
15
+ * living document and two features editing one requirement would otherwise
16
+ * overwrite each other silently. A flow has none of that: the file is one
17
+ * ordered hop sequence, the merge is a whole-file copy, and there is nothing to
18
+ * merge partially. Rewriting a living flow through a feature would be a silent
19
+ * whole-file replacement with no pin to collide on, where editing
20
+ * `architecture/usecases/<name>.likec4` directly in the same pull request
21
+ * produces an ordinary git conflict a human resolves. `usecase.flow-exists`
22
+ * refuses the first and names the second — the same shape, and the same
23
+ * severity, as `glossary.term-exists`.
24
+ *
25
+ * WHY A FILE OF ITS OWN AND NOT `delta.likec4`. The C4 delta is a MODEL delta:
26
+ * it re-declares the landscape's own identifiers to anchor its new edges, and it
27
+ * carries its own `specification` block because LikeC4 parses it standalone.
28
+ * Both make it unstageable beside the landscape in one project — a duplicate
29
+ * specification is an error blamed on both files, and a re-declared element is a
30
+ * duplicate id. A views-only document declares neither, which is exactly why
31
+ * living use cases already live in files of their own; the feature-local slot
32
+ * mirrors that layout so the merge is a copy and the overlay is a stage.
33
+ * `core/c4/splice/delta-blocks.ts` goes on refusing a `dynamic view` inside
34
+ * `delta.likec4` and now names this slot as the place it belongs.
35
+ *
36
+ * ONE CALL for the walk. `architectureDocuments` already implements "every
37
+ * `.likec4` under a directory, recursively, in a stable order, and an absent
38
+ * directory is an empty list" — which is the whole of what a feature's flow
39
+ * directory is. A second walk would be two implementations free to disagree
40
+ * about what a flow file is, and they would disagree first about nesting.
41
+ */
42
+ import { existsSync } from "node:fs";
43
+ import { join, relative, resolve } from "node:path";
44
+ import { architectureDocuments } from "../../c4/project/documents.js";
45
+ import { architectureDir } from "../../c4/project/architecture.js";
46
+ import { featureUseCasesDir } from "../../repo/authored/paths.js";
47
+ import {} from "../../vocabulary/issue.js";
48
+ /** The directory living flows are filed under, inside `architecture/`. */
49
+ export const USECASE_SUBDIR = "usecases";
50
+ /**
51
+ * The flows one feature carries, ordered by path.
52
+ *
53
+ * Zero readdirs when the feature has no `usecases/` directory, which is every
54
+ * feature in a fleet that has not adopted the axis: `architectureDocuments`
55
+ * answers an absent directory with an empty list rather than a throw.
56
+ */
57
+ export async function featureFlows(featureDir) {
58
+ const dir = featureUseCasesDir(featureDir);
59
+ const paths = await architectureDocuments(dir);
60
+ const base = resolve(dir);
61
+ return paths.map((path) => ({ rel: relative(base, path).split(/[\\/]/).join("/"), path }));
62
+ }
63
+ /**
64
+ * The LIVING path a feature-local flow addresses. Spelled here because three
65
+ * readers need it — the refusal below, the archive merge, and the overlay that
66
+ * grades the flow before either — and a reader resolving it differently would
67
+ * refuse a flow the merge then wrote anyway.
68
+ *
69
+ * `rel` is joined un-split, which `node:path` normalizes. What holds the join is
70
+ * PROVENANCE, exactly as it holds `livingTermPath` one axis over: every `rel`
71
+ * here came out of an `architectureDocuments` walk, so each component is a
72
+ * directory entry that exists. No caller takes one from argv.
73
+ */
74
+ export function livingFlowPath(docsDir, rel) {
75
+ return join(architectureDir(docsDir), USECASE_SUBDIR, rel);
76
+ }
77
+ /**
78
+ * `usecase.flow-exists` — a flow this feature introduces that the living
79
+ * `architecture/` already holds.
80
+ *
81
+ * An ERROR and never a warning-that-gates, for `glossary.term-exists`'s reason:
82
+ * there is no legal reading of it. The merge would replace an authored flow
83
+ * wholesale, and no `--approve` makes that a different act than it is. Two
84
+ * features introducing the same flow is the case that makes it load-bearing
85
+ * rather than pedantic — whichever archives first creates the living document,
86
+ * and the second is told before it merges that its own file is the one to
87
+ * delete.
88
+ */
89
+ export async function flowDeltaIssues(docsDir, featureDir) {
90
+ const flows = await featureFlows(featureDir);
91
+ return flows
92
+ .filter((flow) => existsSync(livingFlowPath(docsDir, flow.rel)))
93
+ .map((flow) => ({
94
+ severity: "error",
95
+ code: "usecase.flow-exists",
96
+ subject: flow.rel,
97
+ message: `architecture/${USECASE_SUBDIR}/${flow.rel} already exists, so this feature's flow would replace an authored one wholesale — ` +
98
+ "a feature-local flow INTRODUCES a use case, it does not rewrite one. " +
99
+ `Delete features/<FEAT>/${USECASE_SUBDIR}/${flow.rel} and edit architecture/${USECASE_SUBDIR}/${flow.rel} directly in this same change, ` +
100
+ "where git produces an ordinary conflict if somebody else is editing it too.",
101
+ }));
102
+ }
103
+ //# sourceMappingURL=flows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flows.js","sourceRoot":"","sources":["../../../../src/core/usecases/delta/flows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,EAAc,MAAM,2BAA2B,CAAC;AAEvD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAezC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAsB;IACvD,MAAM,GAAG,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,GAAW;IAC1D,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAgB,EAAE,UAAsB;IAC5E,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;IAC7C,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;SAC/D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE,OAAgB;QAC1B,IAAI,EAAE,qBAA8B;QACpC,OAAO,EAAE,IAAI,CAAC,GAAG;QACjB,OAAO,EACL,gBAAgB,cAAc,IAAI,IAAI,CAAC,GAAG,oFAAoF;YAC9H,uEAAuE;YACvE,0BAA0B,cAAc,IAAI,IAAI,CAAC,GAAG,0BAA0B,cAAc,IAAI,IAAI,CAAC,GAAG,iCAAiC;YACzI,6EAA6E;KAChF,CAAC,CAAC,CAAC;AACR,CAAC","sourcesContent":["/**\n * A feature's own flows: `features/<FEAT>/usecases/<name>.likec4`.\n *\n * A `dynamic view` is the only realizer a cross-service promise has, and until\n * this slot existed it was the one realizer no feature could carry. Every other\n * axis had a delta — a C4 delta, a requirement delta on both spec axes, both\n * contract deltas, a capability delta, a glossary term — so the business axis's\n * own headline case had to be written backwards: an analyst adds a capability\n * requirement, the architect answers it with a flow, and the flow could not ship\n * in that change. It merged only after the promise was already living, which\n * meant the promise landed first with nothing keeping it.\n *\n * CREATE-ONLY, for `core/glossary/delta.ts`'s reasons rather than a weaker\n * version of them. A capability document is a DELTA because it merges INTO a\n * living document and two features editing one requirement would otherwise\n * overwrite each other silently. A flow has none of that: the file is one\n * ordered hop sequence, the merge is a whole-file copy, and there is nothing to\n * merge partially. Rewriting a living flow through a feature would be a silent\n * whole-file replacement with no pin to collide on, where editing\n * `architecture/usecases/<name>.likec4` directly in the same pull request\n * produces an ordinary git conflict a human resolves. `usecase.flow-exists`\n * refuses the first and names the second — the same shape, and the same\n * severity, as `glossary.term-exists`.\n *\n * WHY A FILE OF ITS OWN AND NOT `delta.likec4`. The C4 delta is a MODEL delta:\n * it re-declares the landscape's own identifiers to anchor its new edges, and it\n * carries its own `specification` block because LikeC4 parses it standalone.\n * Both make it unstageable beside the landscape in one project — a duplicate\n * specification is an error blamed on both files, and a re-declared element is a\n * duplicate id. A views-only document declares neither, which is exactly why\n * living use cases already live in files of their own; the feature-local slot\n * mirrors that layout so the merge is a copy and the overlay is a stage.\n * `core/c4/splice/delta-blocks.ts` goes on refusing a `dynamic view` inside\n * `delta.likec4` and now names this slot as the place it belongs.\n *\n * ONE CALL for the walk. `architectureDocuments` already implements \"every\n * `.likec4` under a directory, recursively, in a stable order, and an absent\n * directory is an empty list\" — which is the whole of what a feature's flow\n * directory is. A second walk would be two implementations free to disagree\n * about what a flow file is, and they would disagree first about nesting.\n */\nimport { existsSync } from \"node:fs\";\nimport { join, relative, resolve } from \"node:path\";\nimport { architectureDocuments } from \"../../c4/project/documents.js\";\nimport { architectureDir } from \"../../c4/project/architecture.js\";\nimport { featureUseCasesDir } from \"../../repo/authored/paths.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport { type Issue } from \"../../vocabulary/issue.js\";\n\n/** The directory living flows are filed under, inside `architecture/`. */\nexport const USECASE_SUBDIR = \"usecases\";\n\n/** One flow document a feature carries. */\nexport interface FeatureFlow {\n /**\n * The path INSIDE the feature's `usecases/` directory, `/`-separated — the id\n * this axis has, and the same path the merge writes under\n * `architecture/usecases/`. Nesting is spelled by the tree, as it is for every\n * other authored tree in the repo.\n */\n rel: string;\n /** The absolute path of the authored file. */\n path: string;\n}\n\n/**\n * The flows one feature carries, ordered by path.\n *\n * Zero readdirs when the feature has no `usecases/` directory, which is every\n * feature in a fleet that has not adopted the axis: `architectureDocuments`\n * answers an absent directory with an empty list rather than a throw.\n */\nexport async function featureFlows(featureDir: FeatureDir): Promise<FeatureFlow[]> {\n const dir = featureUseCasesDir(featureDir);\n const paths = await architectureDocuments(dir);\n const base = resolve(dir);\n return paths.map((path) => ({ rel: relative(base, path).split(/[\\\\/]/).join(\"/\"), path }));\n}\n\n/**\n * The LIVING path a feature-local flow addresses. Spelled here because three\n * readers need it — the refusal below, the archive merge, and the overlay that\n * grades the flow before either — and a reader resolving it differently would\n * refuse a flow the merge then wrote anyway.\n *\n * `rel` is joined un-split, which `node:path` normalizes. What holds the join is\n * PROVENANCE, exactly as it holds `livingTermPath` one axis over: every `rel`\n * here came out of an `architectureDocuments` walk, so each component is a\n * directory entry that exists. No caller takes one from argv.\n */\nexport function livingFlowPath(docsDir: DocsDir, rel: string): string {\n return join(architectureDir(docsDir), USECASE_SUBDIR, rel);\n}\n\n/**\n * `usecase.flow-exists` — a flow this feature introduces that the living\n * `architecture/` already holds.\n *\n * An ERROR and never a warning-that-gates, for `glossary.term-exists`'s reason:\n * there is no legal reading of it. The merge would replace an authored flow\n * wholesale, and no `--approve` makes that a different act than it is. Two\n * features introducing the same flow is the case that makes it load-bearing\n * rather than pedantic — whichever archives first creates the living document,\n * and the second is told before it merges that its own file is the one to\n * delete.\n */\nexport async function flowDeltaIssues(docsDir: DocsDir, featureDir: FeatureDir): Promise<Issue[]> {\n const flows = await featureFlows(featureDir);\n return flows\n .filter((flow) => existsSync(livingFlowPath(docsDir, flow.rel)))\n .map((flow) => ({\n severity: \"error\" as const,\n code: \"usecase.flow-exists\" as const,\n subject: flow.rel,\n message:\n `architecture/${USECASE_SUBDIR}/${flow.rel} already exists, so this feature's flow would replace an authored one wholesale — ` +\n \"a feature-local flow INTRODUCES a use case, it does not rewrite one. \" +\n `Delete features/<FEAT>/${USECASE_SUBDIR}/${flow.rel} and edit architecture/${USECASE_SUBDIR}/${flow.rel} directly in this same change, ` +\n \"where git produces an ordinary conflict if somebody else is editing it too.\",\n }));\n}\n"]}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * A feature's flows, read against the fleet map AS THIS FEATURE'S MERGE WOULD
3
+ * LEAVE IT.
4
+ *
5
+ * The staging and the merge preview that make that possible are no longer here:
6
+ * they moved to `core/c4/project/staged.ts` when the deployment slot became the
7
+ * second feature-local `.likec4` document, because a second copy of that
8
+ * machinery is the copy that drifts, and the two would disagree first about the
9
+ * merge preview — the one thing both axes' refusals rest on. That module's
10
+ * header carries the WHY of the post-merge corpus; this one keeps what is
11
+ * particular to use cases, which is the interpretation: which of the staged
12
+ * views are this feature's, and what they resolve against.
13
+ *
14
+ * FAIL CLOSED, and not silently. Every failure is the `unreadable` arm carrying
15
+ * LikeC4's own messages: a flow this cannot read is never a flow that grades
16
+ * clean, which is the asymmetry `../fleet.ts` states for the fleet-scope scan.
17
+ */
18
+ import {} from "../../c4/likec4.js";
19
+ import { stageMergedProject } from "../../c4/project/staged.js";
20
+ import { serviceResolver } from "../../c4/resolve/service.js";
21
+ import { isUseCase } from "../fleet.js";
22
+ import { featureFlows, USECASE_SUBDIR } from "./flows.js";
23
+ /**
24
+ * The flows this feature carries, parsed over the post-merge map — or the
25
+ * honest refusal to say.
26
+ *
27
+ * A feature with no `usecases/` directory is `read` with no views, at the cost
28
+ * of one walk over a directory that is not there. That is the axis's
29
+ * per-feature opt-in and the whole price a fleet that has not adopted it pays.
30
+ */
31
+ export async function readFeatureFlows(req) {
32
+ const flows = await featureFlows(req.featureDir);
33
+ if (flows.length === 0) {
34
+ return { kind: "read", views: [], model: { elements: [], relationships: [], known: req.known }, resolve: (id) => id };
35
+ }
36
+ const staged = await stageMergedProject({
37
+ docsDir: req.docsDir,
38
+ featureDir: req.featureDir,
39
+ featureId: req.featureId,
40
+ documents: flows.map((flow) => ({ rel: `${USECASE_SUBDIR}/${flow.rel}`, path: flow.path })),
41
+ ...(req.load === undefined ? {} : { load: req.load }),
42
+ });
43
+ return interpret(staged, req.known);
44
+ }
45
+ /**
46
+ * The staged parse as the use-case axis reads it.
47
+ *
48
+ * THE FEATURE'S OWN FLOWS ONLY. A living use case is the fleet scan's to report,
49
+ * and repeating it here would file somebody else's finding against this
50
+ * feature's gate — the same scoping every other feature-side check keeps.
51
+ */
52
+ function interpret(staged, known) {
53
+ if (staged.kind === "unreadable")
54
+ return { kind: "unreadable", errors: staged.errors };
55
+ const { doc, real, mine } = staged;
56
+ // Through `isUseCase`, because `UseCaseScan.views` is documented as the
57
+ // reserved-tag views ONLY and this is the type's second producer. The
58
+ // resolvers below filter on the prefix anyway, so no answer changes today —
59
+ // but `flowsClaiming` and `servicesInFlowsClaiming` take a `UseCaseScan` and
60
+ // read `scan.views` straight, and the whole point of exporting one predicate
61
+ // was that a second spelling of the opt-in drifts.
62
+ const views = (doc.views ?? [])
63
+ .filter((view) => view.sourcePath !== undefined && mine.has(view.sourcePath))
64
+ .filter(isUseCase);
65
+ return {
66
+ kind: "read",
67
+ views: views.map((view) => ({ ...view, sourcePath: view.sourcePath === undefined ? view.sourcePath : real(view.sourcePath) })),
68
+ model: { elements: doc.elements, relationships: doc.relationships, known },
69
+ resolve: serviceResolver(doc.elements, known),
70
+ };
71
+ }
72
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../../../src/core/usecases/delta/overlay.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A feature's flows, read against the fleet map AS THIS FEATURE'S MERGE WOULD\n * LEAVE IT.\n *\n * The staging and the merge preview that make that possible are no longer here:\n * they moved to `core/c4/project/staged.ts` when the deployment slot became the\n * second feature-local `.likec4` document, because a second copy of that\n * machinery is the copy that drifts, and the two would disagree first about the\n * merge preview — the one thing both axes' refusals rest on. That module's\n * header carries the WHY of the post-merge corpus; this one keeps what is\n * particular to use cases, which is the interpretation: which of the staged\n * views are this feature's, and what they resolve against.\n *\n * FAIL CLOSED, and not silently. Every failure is the `unreadable` arm carrying\n * LikeC4's own messages: a flow this cannot read is never a flow that grades\n * clean, which is the asymmetry `../fleet.ts` states for the fleet-scope scan.\n */\nimport { type LoadedDoc } from \"../../c4/likec4.js\";\nimport { stageMergedProject, type StagedProject } from \"../../c4/project/staged.js\";\nimport { serviceResolver } from \"../../c4/resolve/service.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport { isUseCase, type UseCaseScan } from \"../fleet.js\";\nimport { featureFlows, USECASE_SUBDIR } from \"./flows.js\";\n\n/** Everything the overlay needs, and nothing it would have to read twice. */\nexport interface FlowOverlayRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n /** The feature's id — the tag that selects the delta's own additions. */\n featureId: string;\n /**\n * The enumerated fleet, for the element→service resolver. It rides in for the\n * reason every other edge join carries it: without it a hop drawn into a\n * modelled container `payment.api` resolves to a service called \"api\" that\n * has never existed.\n */\n known: ReadonlySet<string>;\n /**\n * The caller's memoised LikeC4 read, when it has one. Only `delta.likec4` goes\n * through it, and only to compute the merge preview — but the caller\n * (`featureCoherence`) has already loaded that exact document, and loading one\n * spins a fresh Langium workspace, which `core/coherence/coherence.ts` calls\n * the dominant per-feature cost.\n *\n * A FUNCTION rather than the `FleetContext` itself, deliberately: this package\n * has no edge to `core/fleet-context.ts`, one memoised read does not justify\n * adding one, and the edge would push `core/usecases/` up a DAG level for\n * nothing. Absent, the load happens directly, which is right for a caller\n * running outside an invocation index.\n */\n load?: (path: string) => Promise<LoadedDoc>;\n}\n\n/**\n * The flows this feature carries, parsed over the post-merge map — or the\n * honest refusal to say.\n *\n * A feature with no `usecases/` directory is `read` with no views, at the cost\n * of one walk over a directory that is not there. That is the axis's\n * per-feature opt-in and the whole price a fleet that has not adopted it pays.\n */\nexport async function readFeatureFlows(req: FlowOverlayRequest): Promise<UseCaseScan> {\n const flows = await featureFlows(req.featureDir);\n if (flows.length === 0) {\n return { kind: \"read\", views: [], model: { elements: [], relationships: [], known: req.known }, resolve: (id) => id };\n }\n const staged = await stageMergedProject({\n docsDir: req.docsDir,\n featureDir: req.featureDir,\n featureId: req.featureId,\n documents: flows.map((flow) => ({ rel: `${USECASE_SUBDIR}/${flow.rel}`, path: flow.path })),\n ...(req.load === undefined ? {} : { load: req.load }),\n });\n return interpret(staged, req.known);\n}\n\n/**\n * The staged parse as the use-case axis reads it.\n *\n * THE FEATURE'S OWN FLOWS ONLY. A living use case is the fleet scan's to report,\n * and repeating it here would file somebody else's finding against this\n * feature's gate — the same scoping every other feature-side check keeps.\n */\nfunction interpret(staged: StagedProject, known: ReadonlySet<string>): UseCaseScan {\n if (staged.kind === \"unreadable\") return { kind: \"unreadable\", errors: staged.errors };\n const { doc, real, mine } = staged;\n // Through `isUseCase`, because `UseCaseScan.views` is documented as the\n // reserved-tag views ONLY and this is the type's second producer. The\n // resolvers below filter on the prefix anyway, so no answer changes today —\n // but `flowsClaiming` and `servicesInFlowsClaiming` take a `UseCaseScan` and\n // read `scan.views` straight, and the whole point of exporting one predicate\n // was that a second spelling of the opt-in drifts.\n const views = (doc.views ?? [])\n .filter((view) => view.sourcePath !== undefined && mine.has(view.sourcePath))\n .filter(isUseCase);\n return {\n kind: \"read\",\n views: views.map((view) => ({ ...view, sourcePath: view.sourcePath === undefined ? view.sourcePath : real(view.sourcePath) })),\n model: { elements: doc.elements, relationships: doc.relationships, known },\n resolve: serviceResolver(doc.elements, known),\n };\n}\n"]}
@@ -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/resolve/steps.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"]}