@ybotok/loam 0.2.0-alpha.1 → 0.2.0-alpha.13

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 (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Which `.likec4` files beside a `model.likec4` the RENDERER actually loads —
3
+ * the `exclude` list of the service's own `likec4.config.json`.
4
+ *
5
+ * loam writes that file with two keys and never reads it back
6
+ * (`core/repo/tree/render/projects.ts` states the ownership rule), but a team
7
+ * may add keys to it, and `exclude` is the one that changes what the project
8
+ * holds. Until this module existed the service-flow reader staged every sibling
9
+ * regardless: a view the renderer never sees was graded, and the finding it
10
+ * earned said the project is "read the way the renderer reads it" and that the
11
+ * view still renders — three claims about a file nobody would ever open
12
+ * (verification 2026-09-04, D10).
13
+ *
14
+ * STANDALONE MODELS ONLY, which is where this file IS the project config. A
15
+ * model that extends the map is read inside the ROOT project, whose own
16
+ * `exclude` decides what it holds; a per-service config beside such a model is
17
+ * a stray with a finding of its own (`service.likec4-config-stray`), and
18
+ * honouring its list here would silence a document the renderer really does
19
+ * load.
20
+ *
21
+ * THE MATCHER IS THE ONE MEASURED, at the likec4 1.59.2 pin, on a scratch
22
+ * service directory holding `model.likec4`, `views.likec4` and
23
+ * `usecases/flow.likec4` (`npx likec4 validate <dir>`, counting "found N source
24
+ * files"): an entry drops a file when the file's directory-relative path STARTS
25
+ * WITH the entry (`views`, `views.`, `views.likec4`, `usecases`, `usecases/`,
26
+ * `usecases/flow` each dropped one; `ews.likec4` dropped none), or when it
27
+ * matches as a glob whose `*` crosses `/` (`*.likec4` dropped all THREE), or
28
+ * when a leading RECURSIVE segment — a doubled star and a slash — stands for
29
+ * the zero or more directories above the file: that segment before `*.likec4`
30
+ * dropped all three, and before `usecases` (with a recursive tail) or
31
+ * `views.likec4` it dropped one each. All three halves are implemented because
32
+ * all three were observed; nothing here interprets more than that, and an entry
33
+ * loam cannot match leaves the file loaded — grading a file the renderer skips
34
+ * is a wrong sentence, and skipping one it reads is a missing finding, which is
35
+ * the worse of the two.
36
+ */
37
+ import { readFile } from "node:fs/promises";
38
+ import { relative } from "node:path";
39
+ import { isRecord } from "../../kernel/records.js";
40
+ import { excludingPath } from "../../c4/root-project/exclude.js";
41
+ /** The per-service project file, by the name `core/repo/tree/render/projects.ts` writes it under. */
42
+ const CONFIG = "likec4.config.json";
43
+ /**
44
+ * The service project's `exclude` entries, or `[]` when there is no list to
45
+ * read — absent, unreadable, not JSON, or an `exclude` that is not a list of
46
+ * strings. Failing open is right here and nowhere else: the answer decides
47
+ * whether loam GRADES a document, and an empty list grades everything, which is
48
+ * exactly what loam did before this module and never a new silence.
49
+ */
50
+ export async function serviceProjectExclude(dir) {
51
+ const text = await readFile(`${dir}/${CONFIG}`, "utf8").catch(() => null);
52
+ if (text === null)
53
+ return [];
54
+ let parsed;
55
+ try {
56
+ parsed = JSON.parse(text);
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ if (!isRecord(parsed))
62
+ return [];
63
+ const exclude = parsed["exclude"];
64
+ if (!Array.isArray(exclude))
65
+ return [];
66
+ const entries = exclude;
67
+ return entries.filter((entry) => typeof entry === "string" && entry.trim() !== "");
68
+ }
69
+ /** The siblings that survive the list — absolute paths in, absolute paths out, order kept. */
70
+ export function keepIncluded(dir, exclude, paths) {
71
+ if (exclude.length === 0)
72
+ return [...paths];
73
+ return paths.filter((path) => !excluded(exclude, relative(dir, path).split(/[\\/]/).join("/")));
74
+ }
75
+ /**
76
+ * Three rungs, because three separate spellings were measured hiding a file and
77
+ * no one of them subsumes the others.
78
+ *
79
+ * - the bare PREFIX (`views`, `usecases/flow`), which is the renderer's own
80
+ * behaviour and matches nothing a glob would;
81
+ * - `excludingPath`, the segment walk `core/c4/root-project/exclude.ts` already
82
+ * owns, which is what reads a doubled star anywhere in the entry — the three
83
+ * recursive spellings the docstring above measured each hide what the
84
+ * renderer hides, and the `globRe` half alone matched NONE of them, because a
85
+ * leading recursive segment compiled to a REQUIRED slash. loam therefore
86
+ * graded a file in no project it may claim to be reading, and gated the run
87
+ * on it (verification 2026-09-04);
88
+ * - `globRe`, kept for the one case the segment walk cannot express: a `*` that
89
+ * CROSSES `/`, which is what `*.likec4` hiding `usecases/flow.likec4` was
90
+ * measured doing.
91
+ *
92
+ * A union only ever hides more, and the module's own rule says the worse of the
93
+ * two mistakes is skipping a file the renderer reads; each rung above is a
94
+ * measurement rather than an interpretation, so the union stays honest.
95
+ */
96
+ function excluded(exclude, rel) {
97
+ return exclude.some((raw) => {
98
+ const entry = raw.replace(/^\.\//, "");
99
+ if (entry === "")
100
+ return false;
101
+ if (rel.startsWith(entry))
102
+ return true;
103
+ if (excludingPath([entry], rel) !== null)
104
+ return true;
105
+ return entry.includes("*") && globRe(entry).test(rel);
106
+ });
107
+ }
108
+ /**
109
+ * One entry as a whole-path regular expression, with `*` crossing `/` — which
110
+ * is what `*.likec4` excluding `usecases/flow.likec4` measures. Built per call
111
+ * rather than cached: a `RegExp` is cheap beside the readdir that produced the
112
+ * paths, and a module-level cache keyed on a string is the shared state
113
+ * AGENTS.md warns about.
114
+ */
115
+ function globRe(entry) {
116
+ const source = entry.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*+/g, ".*").replace(/\?/g, ".");
117
+ return new RegExp(`^${source}$`);
118
+ }
119
+ //# sourceMappingURL=exclude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exclude.js","sourceRoot":"","sources":["../../../../src/core/usecases/service/exclude.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which `.likec4` files beside a `model.likec4` the RENDERER actually loads —\n * the `exclude` list of the service's own `likec4.config.json`.\n *\n * loam writes that file with two keys and never reads it back\n * (`core/repo/tree/render/projects.ts` states the ownership rule), but a team\n * may add keys to it, and `exclude` is the one that changes what the project\n * holds. Until this module existed the service-flow reader staged every sibling\n * regardless: a view the renderer never sees was graded, and the finding it\n * earned said the project is \"read the way the renderer reads it\" and that the\n * view still renders — three claims about a file nobody would ever open\n * (verification 2026-09-04, D10).\n *\n * STANDALONE MODELS ONLY, which is where this file IS the project config. A\n * model that extends the map is read inside the ROOT project, whose own\n * `exclude` decides what it holds; a per-service config beside such a model is\n * a stray with a finding of its own (`service.likec4-config-stray`), and\n * honouring its list here would silence a document the renderer really does\n * load.\n *\n * THE MATCHER IS THE ONE MEASURED, at the likec4 1.59.2 pin, on a scratch\n * service directory holding `model.likec4`, `views.likec4` and\n * `usecases/flow.likec4` (`npx likec4 validate <dir>`, counting \"found N source\n * files\"): an entry drops a file when the file's directory-relative path STARTS\n * WITH the entry (`views`, `views.`, `views.likec4`, `usecases`, `usecases/`,\n * `usecases/flow` each dropped one; `ews.likec4` dropped none), or when it\n * matches as a glob whose `*` crosses `/` (`*.likec4` dropped all THREE), or\n * when a leading RECURSIVE segment — a doubled star and a slash — stands for\n * the zero or more directories above the file: that segment before `*.likec4`\n * dropped all three, and before `usecases` (with a recursive tail) or\n * `views.likec4` it dropped one each. All three halves are implemented because\n * all three were observed; nothing here interprets more than that, and an entry\n * loam cannot match leaves the file loaded — grading a file the renderer skips\n * is a wrong sentence, and skipping one it reads is a missing finding, which is\n * the worse of the two.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport { isRecord } from \"../../kernel/records.js\";\nimport { excludingPath } from \"../../c4/root-project/exclude.js\";\nimport type { ServiceDir } from \"../../kernel/ids/dirs.js\";\n\n/** The per-service project file, by the name `core/repo/tree/render/projects.ts` writes it under. */\nconst CONFIG = \"likec4.config.json\";\n\n/**\n * The service project's `exclude` entries, or `[]` when there is no list to\n * read — absent, unreadable, not JSON, or an `exclude` that is not a list of\n * strings. Failing open is right here and nowhere else: the answer decides\n * whether loam GRADES a document, and an empty list grades everything, which is\n * exactly what loam did before this module and never a new silence.\n */\nexport async function serviceProjectExclude(dir: ServiceDir): Promise<string[]> {\n const text = await readFile(`${dir}/${CONFIG}`, \"utf8\").catch(() => null);\n if (text === null) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return [];\n }\n if (!isRecord(parsed)) return [];\n const exclude: unknown = parsed[\"exclude\"];\n if (!Array.isArray(exclude)) return [];\n const entries: unknown[] = exclude;\n return entries.filter((entry): entry is string => typeof entry === \"string\" && entry.trim() !== \"\");\n}\n\n/** The siblings that survive the list — absolute paths in, absolute paths out, order kept. */\nexport function keepIncluded(dir: ServiceDir, exclude: readonly string[], paths: readonly string[]): string[] {\n if (exclude.length === 0) return [...paths];\n return paths.filter((path) => !excluded(exclude, relative(dir, path).split(/[\\\\/]/).join(\"/\")));\n}\n\n/**\n * Three rungs, because three separate spellings were measured hiding a file and\n * no one of them subsumes the others.\n *\n * - the bare PREFIX (`views`, `usecases/flow`), which is the renderer's own\n * behaviour and matches nothing a glob would;\n * - `excludingPath`, the segment walk `core/c4/root-project/exclude.ts` already\n * owns, which is what reads a doubled star anywhere in the entry — the three\n * recursive spellings the docstring above measured each hide what the\n * renderer hides, and the `globRe` half alone matched NONE of them, because a\n * leading recursive segment compiled to a REQUIRED slash. loam therefore\n * graded a file in no project it may claim to be reading, and gated the run\n * on it (verification 2026-09-04);\n * - `globRe`, kept for the one case the segment walk cannot express: a `*` that\n * CROSSES `/`, which is what `*.likec4` hiding `usecases/flow.likec4` was\n * measured doing.\n *\n * A union only ever hides more, and the module's own rule says the worse of the\n * two mistakes is skipping a file the renderer reads; each rung above is a\n * measurement rather than an interpretation, so the union stays honest.\n */\nfunction excluded(exclude: readonly string[], rel: string): boolean {\n return exclude.some((raw) => {\n const entry = raw.replace(/^\\.\\//, \"\");\n if (entry === \"\") return false;\n if (rel.startsWith(entry)) return true;\n if (excludingPath([entry], rel) !== null) return true;\n return entry.includes(\"*\") && globRe(entry).test(rel);\n });\n}\n\n/**\n * One entry as a whole-path regular expression, with `*` crossing `/` — which\n * is what `*.likec4` excluding `usecases/flow.likec4` measures. Built per call\n * rather than cached: a `RegExp` is cheap beside the readdir that produced the\n * paths, and a module-level cache keyed on a string is the shared state\n * AGENTS.md warns about.\n */\nfunction globRe(entry: string): RegExp {\n const source = entry.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*+/g, \".*\").replace(/\\?/g, \".\");\n return new RegExp(`^${source}$`);\n}\n"]}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * A service's OWN use cases: the `dynamic view`s its LikeC4 project declares,
3
+ * read the way the renderer reads that project.
4
+ *
5
+ * THE SLOT IS THE PROJECT. `loam subsystem sync` writes a
6
+ * `services/<…>/<svc>/likec4.config.json` that registers every `.likec4` under
7
+ * the service directory as one LikeC4 project, so the renderer already shows a
8
+ * `views.likec4` beside the model, or a `usecases/<name>.likec4` under it. loam
9
+ * reads exactly that set — `model.likec4` plus every sibling, recursively, minus
10
+ * whatever that project file's own `exclude` drops (`./exclude.ts`) — and
11
+ * nothing else, because a slot narrower than the renderer's would leave a flow
12
+ * that renders and is graded by nothing, which is the report this module
13
+ * answers: an intra-service flow behind an `arch.spec.md` requirement whose
14
+ * `Covers:` names containers had no home. `architecture/usecases/` is a
15
+ * separate project that cannot resolve a container (one such file turned a
16
+ * whole fleet `landscape.invalid`), and the author's workaround beside the
17
+ * model was invisible to every check. The read is NOT gated on the project
18
+ * file's presence: that file is create-only and a team may delete it, its
19
+ * absence has a warning of its own (`service.likec4-config-missing`), and a
20
+ * flow the renderer cannot show yet is still a flow loam can grade.
21
+ *
22
+ * MODEL FACTS STAY SINGLE-FILE. `c4.valid`'s counts, `Covers:` resolution,
23
+ * `health.dependency-unmodelled` and every other model reader keep the
24
+ * single-file load the service target already made; the project is loaded here
25
+ * for the VIEWS and their `sourcePath` only, and only when there is something
26
+ * to grade. A sibling that declares elements extends what a hop may name, and
27
+ * that is the renderer's reading too.
28
+ *
29
+ * TWO SHAPES, ONE SLOT. A model that stands alone is a project of its own
30
+ * directory, exactly as above. A model that EXTENDS the fleet map is not: its
31
+ * containers are declared inside an element the map owns, so a flow beside it
32
+ * naming one of those containers only resolves in a project that also holds the
33
+ * map (`c4/service-model/documents.ts`, `serviceFlowDocuments`). That project is
34
+ * staged from the DOCS ROOT, so every view comes back spelled
35
+ * `services/<tree>/usecases/x.likec4` — and the service prefix is stripped here,
36
+ * because every finding this scan feeds names a file relative to the service
37
+ * directory and a reader must not have to work out which spelling they were
38
+ * given. The same prefix is the FILTER: the map's own `architecture/usecases/`
39
+ * flows are in that project too, and without it every service in the fleet would
40
+ * be graded for every fleet-level use case.
41
+ *
42
+ * THE CHEAP GATE, in order, so a fleet without the axis pays one recursive
43
+ * readdir per service and nothing else:
44
+ *
45
+ * 1. no sibling → nothing new is loaded, and the model's own tagged views are
46
+ * graded at zero cost against the project the model was READ in. For a
47
+ * standalone model that is the single-file load already in hand, which IS
48
+ * its whole project; for an extending one it is `ServiceModel.project`, the
49
+ * map and this model together — NOT the service's own slice of it, which is
50
+ * what the caller passes as `model` and what every count and `Covers:` line
51
+ * is answered from. A hop backed by a map edge is backed wherever the view
52
+ * is written, and grading the two arms against different documents made one
53
+ * view `usecase.step-unbacked` in model.likec4 and clean in a sibling. A
54
+ * standalone model's views came back named `source.c4`
55
+ * (`core/usecases/place.ts` records the rule) and an extending model's carry
56
+ * their docs-relative path, so the file is spelled here, where it is known,
57
+ * for both: `model.likec4`;
58
+ * 2. siblings, no tagged view in the model, and no reserved tag REFERENCED in
59
+ * any document — a `#` then `cap-`, `req-` or `suite-` (`mentionsTagPrefix`,
60
+ * the fleet's own byte gate) → nothing opted in, no workspace. An untagged
61
+ * `views.likec4` is somebody's hand-drawn diagram, never graded, exactly as
62
+ * an untagged fleet view is — and its parse errors are the renderer's
63
+ * business until a view opts the project in. That includes one whose prose
64
+ * names a prefix without the `#` (a title about an e2e-suite job): the gate
65
+ * used to open on the bare substring, and such a sibling that did not parse
66
+ * became `usecase.flow-invalid` over a diagram that opted nothing in;
67
+ * 3. otherwise `loadProject` over model + siblings, the way the renderer reads
68
+ * them. `own` and the project's views are never merged: when siblings
69
+ * exist the project is the whole answer, which is what keeps one view from
70
+ * being graded twice.
71
+ *
72
+ * A REQUEST RECORD rather than three parameters, for the reason
73
+ * docs/CODE-STYLE.md gives: the paths, the single-file doc and the fleet set
74
+ * are only ever passed together, and a caller handing in a doc loaded from
75
+ * some other path is exactly the inconsistent pair a record makes
76
+ * unrepresentable.
77
+ */
78
+ import { relative } from "node:path";
79
+ import { architectureDocuments } from "../../c4/project/documents.js";
80
+ import { asLoadedDoc, loadProject } from "../../c4/project/load.js";
81
+ import { serviceFlowDocuments } from "../../c4/service-model/documents.js";
82
+ import { ARTIFACT_FILES } from "../../repo/paths.js";
83
+ import { isUseCase, mentionsTagPrefix } from "../fleet.js";
84
+ import { keepIncluded, serviceProjectExclude } from "./exclude.js";
85
+ /**
86
+ * The service's declared use cases, with the model they are drawn over.
87
+ *
88
+ * Reads only, throws nothing: every failure is the `unreadable` arm. The caller
89
+ * has already established that `model.likec4` parses on its own — a project
90
+ * whose model is broken is `c4.invalid`'s business, and loading it again here
91
+ * would only repeat that cascade under a second code.
92
+ */
93
+ export async function readServiceFlows(req) {
94
+ const found = await architectureDocuments(req.paths.dir, [req.paths.model]);
95
+ // What the RENDERER holds, for the standalone shape where this directory's
96
+ // own `likec4.config.json` IS the project config: a sibling its `exclude`
97
+ // covers is in no project loam may claim to be reading (`./exclude.ts`). The
98
+ // extending shape reads the root project instead, so its list does not apply
99
+ // — a per-service config beside an extending model is `service.likec4-config-stray`.
100
+ const siblings = req.extending === undefined
101
+ ? keepIncluded(req.paths.dir, await serviceProjectExclude(req.paths.dir), found)
102
+ : found;
103
+ // The scope of the no-sibling arm, and it is the PROJECT for both shapes: the
104
+ // single-file load for a standalone model (which is its whole project), and
105
+ // the map-plus-model load for an extending one. Reading `req.model` there for
106
+ // an extending model read its own slice instead, so one view was graded two
107
+ // different ways depending on which file it was written in.
108
+ const alone = req.extending?.project ?? req.model;
109
+ const scope = { elements: alone.elements, relationships: alone.relationships, known: req.known };
110
+ const own = (req.model.views ?? []).filter(isUseCase);
111
+ if (siblings.length === 0) {
112
+ if (own.length === 0)
113
+ return { kind: "none" };
114
+ return { kind: "read", views: own.map((view) => ({ ...view, sourcePath: ARTIFACT_FILES.model })), model: scope };
115
+ }
116
+ if (own.length === 0 && !(await mentionsTagPrefix(siblings)))
117
+ return { kind: "none" };
118
+ const base = req.extending?.docsDir ?? req.paths.dir;
119
+ const documents = req.extending === undefined
120
+ ? [req.paths.model, ...siblings]
121
+ : await serviceFlowDocuments(req.extending.docsDir, req.paths);
122
+ const project = await loadProject(base, documents);
123
+ if (!project.clean) {
124
+ // Each error already carries the real path of its document (`loadProject`
125
+ // rewrites the staged path back), and the flattening is `asLoadedDoc`'s —
126
+ // the fleet's own path-sorted order, taken from the one place it is
127
+ // spelled rather than re-spelled here: the caller names the FIRST broken
128
+ // file, and a second sort rule would let the service target name a
129
+ // different first file than the fleet target names for the same project.
130
+ return { kind: "unreadable", errors: asLoadedDoc(project).errors };
131
+ }
132
+ // The prefix the service's own documents are spelled under inside this
133
+ // project: empty for a standalone model (the project IS the directory, so
134
+ // every view in it is this service's) and `services/<tree>/` for an extending
135
+ // one, whose project also holds the whole fleet map.
136
+ const prefix = req.extending === undefined ? "" : `${relative(base, req.paths.dir).split(/[\\/]/).join("/")}/`;
137
+ return {
138
+ kind: "read",
139
+ views: rebase(project.views.filter(isUseCase), prefix),
140
+ model: { elements: project.elements, relationships: project.relationships, known: req.known },
141
+ };
142
+ }
143
+ /**
144
+ * The views this SERVICE declares, spelled relative to its own directory.
145
+ *
146
+ * With an empty prefix — the standalone shape — this is the identity, including
147
+ * for a view whose `sourcePath` is absent: that project holds nothing but the
148
+ * service's own files, so there is nothing to attribute and nothing to strip.
149
+ *
150
+ * With a prefix, it is both the filter and the rebase, and both halves are one
151
+ * rule about which document belongs to whom. The project also holds the fleet
152
+ * map, so `architecture/usecases/checkout.likec4` is in it — and without the
153
+ * filter every service in the fleet would be graded for every fleet-level use
154
+ * case. A view carrying no `sourcePath` cannot be attributed at all and is
155
+ * dropped for the same reason: the path is the only evidence there is.
156
+ */
157
+ function rebase(views, prefix) {
158
+ if (prefix === "")
159
+ return [...views];
160
+ return views
161
+ .filter((view) => view.sourcePath?.startsWith(prefix) === true)
162
+ .map((view) => ({ ...view, sourcePath: (view.sourcePath ?? "").slice(prefix.length) }));
163
+ }
164
+ //# sourceMappingURL=flows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flows.js","sourceRoot":"","sources":["../../../../src/core/usecases/service/flows.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A service's OWN use cases: the `dynamic view`s its LikeC4 project declares,\n * read the way the renderer reads that project.\n *\n * THE SLOT IS THE PROJECT. `loam subsystem sync` writes a\n * `services/<…>/<svc>/likec4.config.json` that registers every `.likec4` under\n * the service directory as one LikeC4 project, so the renderer already shows a\n * `views.likec4` beside the model, or a `usecases/<name>.likec4` under it. loam\n * reads exactly that set — `model.likec4` plus every sibling, recursively, minus\n * whatever that project file's own `exclude` drops (`./exclude.ts`) — and\n * nothing else, because a slot narrower than the renderer's would leave a flow\n * that renders and is graded by nothing, which is the report this module\n * answers: an intra-service flow behind an `arch.spec.md` requirement whose\n * `Covers:` names containers had no home. `architecture/usecases/` is a\n * separate project that cannot resolve a container (one such file turned a\n * whole fleet `landscape.invalid`), and the author's workaround beside the\n * model was invisible to every check. The read is NOT gated on the project\n * file's presence: that file is create-only and a team may delete it, its\n * absence has a warning of its own (`service.likec4-config-missing`), and a\n * flow the renderer cannot show yet is still a flow loam can grade.\n *\n * MODEL FACTS STAY SINGLE-FILE. `c4.valid`'s counts, `Covers:` resolution,\n * `health.dependency-unmodelled` and every other model reader keep the\n * single-file load the service target already made; the project is loaded here\n * for the VIEWS and their `sourcePath` only, and only when there is something\n * to grade. A sibling that declares elements extends what a hop may name, and\n * that is the renderer's reading too.\n *\n * TWO SHAPES, ONE SLOT. A model that stands alone is a project of its own\n * directory, exactly as above. A model that EXTENDS the fleet map is not: its\n * containers are declared inside an element the map owns, so a flow beside it\n * naming one of those containers only resolves in a project that also holds the\n * map (`c4/service-model/documents.ts`, `serviceFlowDocuments`). That project is\n * staged from the DOCS ROOT, so every view comes back spelled\n * `services/<tree>/usecases/x.likec4` — and the service prefix is stripped here,\n * because every finding this scan feeds names a file relative to the service\n * directory and a reader must not have to work out which spelling they were\n * given. The same prefix is the FILTER: the map's own `architecture/usecases/`\n * flows are in that project too, and without it every service in the fleet would\n * be graded for every fleet-level use case.\n *\n * THE CHEAP GATE, in order, so a fleet without the axis pays one recursive\n * readdir per service and nothing else:\n *\n * 1. no sibling → nothing new is loaded, and the model's own tagged views are\n * graded at zero cost against the project the model was READ in. For a\n * standalone model that is the single-file load already in hand, which IS\n * its whole project; for an extending one it is `ServiceModel.project`, the\n * map and this model together — NOT the service's own slice of it, which is\n * what the caller passes as `model` and what every count and `Covers:` line\n * is answered from. A hop backed by a map edge is backed wherever the view\n * is written, and grading the two arms against different documents made one\n * view `usecase.step-unbacked` in model.likec4 and clean in a sibling. A\n * standalone model's views came back named `source.c4`\n * (`core/usecases/place.ts` records the rule) and an extending model's carry\n * their docs-relative path, so the file is spelled here, where it is known,\n * for both: `model.likec4`;\n * 2. siblings, no tagged view in the model, and no reserved tag REFERENCED in\n * any document — a `#` then `cap-`, `req-` or `suite-` (`mentionsTagPrefix`,\n * the fleet's own byte gate) → nothing opted in, no workspace. An untagged\n * `views.likec4` is somebody's hand-drawn diagram, never graded, exactly as\n * an untagged fleet view is — and its parse errors are the renderer's\n * business until a view opts the project in. That includes one whose prose\n * names a prefix without the `#` (a title about an e2e-suite job): the gate\n * used to open on the bare substring, and such a sibling that did not parse\n * became `usecase.flow-invalid` over a diagram that opted nothing in;\n * 3. otherwise `loadProject` over model + siblings, the way the renderer reads\n * them. `own` and the project's views are never merged: when siblings\n * exist the project is the whole answer, which is what keeps one view from\n * being graded twice.\n *\n * A REQUEST RECORD rather than three parameters, for the reason\n * docs/CODE-STYLE.md gives: the paths, the single-file doc and the fleet set\n * are only ever passed together, and a caller handing in a doc loaded from\n * some other path is exactly the inconsistent pair a record makes\n * unrepresentable.\n */\nimport { relative } from \"node:path\";\nimport type { LikeC4Error, LoadedDoc } from \"../../c4/likec4.js\";\nimport type { ParsedView } from \"../../c4/parsed/dynamic-views.js\";\nimport { architectureDocuments } from \"../../c4/project/documents.js\";\nimport { asLoadedDoc, loadProject } from \"../../c4/project/load.js\";\nimport { serviceFlowDocuments } from \"../../c4/service-model/documents.js\";\nimport type { StepScope } from \"../../c4/resolve/steps.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\nimport { ARTIFACT_FILES, type ServicePaths } from \"../../repo/paths.js\";\nimport { isUseCase, mentionsTagPrefix } from \"../fleet.js\";\nimport { keepIncluded, serviceProjectExclude } from \"./exclude.js\";\n\nexport interface ServiceFlowRequest {\n paths: ServicePaths;\n /**\n * The single-file load the service target already made and has verified\n * parses. Its views are the project when no sibling exists; its elements are\n * the model a no-sibling flow is graded over.\n */\n model: LoadedDoc;\n /** The enumerated fleet, for `serviceResolver`'s reason: without it a container resolves to a service that has never existed. */\n known: ReadonlySet<string>;\n /**\n * Set only when this model EXTENDS the fleet map, and then both halves of it\n * — a record rather than two optional fields, for docs/CODE-STYLE.md's reason:\n * they are only ever passed together, and a caller that supplied one without\n * the other is exactly the inconsistent pair a record makes unrepresentable.\n *\n * `docsDir` decides which documents the sibling project holds (the map as\n * well, or the directory alone) and which root they are staged from, and\n * therefore how LikeC4 spells every `sourcePath` that comes back.\n *\n * `project` is the per-service project the model was READ in — the map plus\n * this model (`ServiceModel.project`) — and it is the scope a hop resolves\n * against when there is no sibling file. `model` above is then the service's\n * own SLICE of that project, which is the right answer for every count and\n * every `Covers:` line and the wrong one here: a hop backed by a MAP edge is\n * backed, and grading it against the slice reported `usecase.step-unbacked`\n * (exit 1) for a view in model.likec4 that was clean the moment the same view\n * moved to a sibling file, where the loaded project holds the map. One\n * question, two answers, decided by which file the author happened to write\n * the view in.\n *\n * Absent means the standalone shape — the service directory is the project and\n * the single-file load IS that project, exactly as it has always been.\n */\n extending?: { docsDir: DocsDir; project: LoadedDoc };\n}\n\n/**\n * What a service's flows are, or the honest refusal to say.\n *\n * Three variants rather than a record with flags, for `UseCaseScan`'s reason\n * one package up: a project that did not parse has no elements to resolve\n * against and no views to grade, and a caller handed `{ views: [], unreadable:\n * true }` is one `.length` check away from reporting a service as flow-free\n * when nobody could read it. `none` is the gate's answer — nothing opted in,\n * nothing was loaded — and it is distinct from `read` with no views (a project\n * that writes a reserved tag reference only outside a view — in a comment, or\n * on an element — and so declares no tagged view), because only the second one\n * cost a workspace.\n */\nexport type ServiceFlowScan =\n | { kind: \"none\" }\n | {\n kind: \"read\";\n /** The reserved-tag views ONLY, each carrying a `sourcePath` relative to the SERVICE directory. */\n views: ParsedView[];\n /** What a hop is attributed against — `known` required, as `UseCaseScan.model` requires it. */\n model: StepScope & { known: ReadonlySet<string> };\n }\n | {\n kind: \"unreadable\";\n /**\n * Every error the project raised, each carrying the real absolute path of\n * the document that raised it, sorted by that path — so a caller can name\n * the file, and a project with two broken documents reports them in a\n * stable order.\n */\n errors: LikeC4Error[];\n };\n\n/**\n * The service's declared use cases, with the model they are drawn over.\n *\n * Reads only, throws nothing: every failure is the `unreadable` arm. The caller\n * has already established that `model.likec4` parses on its own — a project\n * whose model is broken is `c4.invalid`'s business, and loading it again here\n * would only repeat that cascade under a second code.\n */\nexport async function readServiceFlows(req: ServiceFlowRequest): Promise<ServiceFlowScan> {\n const found = await architectureDocuments(req.paths.dir, [req.paths.model]);\n // What the RENDERER holds, for the standalone shape where this directory's\n // own `likec4.config.json` IS the project config: a sibling its `exclude`\n // covers is in no project loam may claim to be reading (`./exclude.ts`). The\n // extending shape reads the root project instead, so its list does not apply\n // — a per-service config beside an extending model is `service.likec4-config-stray`.\n const siblings =\n req.extending === undefined\n ? keepIncluded(req.paths.dir, await serviceProjectExclude(req.paths.dir), found)\n : found;\n // The scope of the no-sibling arm, and it is the PROJECT for both shapes: the\n // single-file load for a standalone model (which is its whole project), and\n // the map-plus-model load for an extending one. Reading `req.model` there for\n // an extending model read its own slice instead, so one view was graded two\n // different ways depending on which file it was written in.\n const alone = req.extending?.project ?? req.model;\n const scope = { elements: alone.elements, relationships: alone.relationships, known: req.known };\n const own = (req.model.views ?? []).filter(isUseCase);\n if (siblings.length === 0) {\n if (own.length === 0) return { kind: \"none\" };\n return { kind: \"read\", views: own.map((view) => ({ ...view, sourcePath: ARTIFACT_FILES.model })), model: scope };\n }\n if (own.length === 0 && !(await mentionsTagPrefix(siblings))) return { kind: \"none\" };\n\n const base = req.extending?.docsDir ?? req.paths.dir;\n const documents =\n req.extending === undefined\n ? [req.paths.model, ...siblings]\n : await serviceFlowDocuments(req.extending.docsDir, req.paths);\n const project = await loadProject(base, documents);\n if (!project.clean) {\n // Each error already carries the real path of its document (`loadProject`\n // rewrites the staged path back), and the flattening is `asLoadedDoc`'s —\n // the fleet's own path-sorted order, taken from the one place it is\n // spelled rather than re-spelled here: the caller names the FIRST broken\n // file, and a second sort rule would let the service target name a\n // different first file than the fleet target names for the same project.\n return { kind: \"unreadable\", errors: asLoadedDoc(project).errors };\n }\n // The prefix the service's own documents are spelled under inside this\n // project: empty for a standalone model (the project IS the directory, so\n // every view in it is this service's) and `services/<tree>/` for an extending\n // one, whose project also holds the whole fleet map.\n const prefix = req.extending === undefined ? \"\" : `${relative(base, req.paths.dir).split(/[\\\\/]/).join(\"/\")}/`;\n return {\n kind: \"read\",\n views: rebase(project.views.filter(isUseCase), prefix),\n model: { elements: project.elements, relationships: project.relationships, known: req.known },\n };\n}\n\n/**\n * The views this SERVICE declares, spelled relative to its own directory.\n *\n * With an empty prefix — the standalone shape — this is the identity, including\n * for a view whose `sourcePath` is absent: that project holds nothing but the\n * service's own files, so there is nothing to attribute and nothing to strip.\n *\n * With a prefix, it is both the filter and the rebase, and both halves are one\n * rule about which document belongs to whom. The project also holds the fleet\n * map, so `architecture/usecases/checkout.likec4` is in it — and without the\n * filter every service in the fleet would be graded for every fleet-level use\n * case. A view carrying no `sourcePath` cannot be attributed at all and is\n * dropped for the same reason: the path is the only evidence there is.\n */\nfunction rebase(views: readonly ParsedView[], prefix: string): ParsedView[] {\n if (prefix === \"\") return [...views];\n return views\n .filter((view) => view.sourcePath?.startsWith(prefix) === true)\n .map((view) => ({ ...view, sourcePath: (view.sourcePath ?? \"\").slice(prefix.length) }));\n}\n"]}
@@ -0,0 +1,83 @@
1
+ import { ancestorIds } from "../../kernel/ids/fqn/ancestors.js";
2
+ import { compareIds } from "../../repo/entries.js";
3
+ import { EXTERNAL_TAG } from "../../vocabulary/maturity.js";
4
+ /**
5
+ * Every endpoint the views' steps draw, resolved through the scan's ONE
6
+ * element→service resolver — the resolver `attributeStep` grades the same hops
7
+ * with, built over the same enumerated fleet (`readUseCases`). A second
8
+ * resolver here could not disagree today and would be a second place to change.
9
+ *
10
+ * AN ENDPOINT THAT RESOLVES TO NO ENUMERATED SERVICE IS REPORTED, NEVER DROPPED.
11
+ * This answer is what a test environment is provisioned from, and a silently
12
+ * shorter list is the one failure that cannot be seen from the list itself: the
13
+ * stand comes up, the missing participant is simply absent, and the journey
14
+ * fails against something nobody was told to start. The actor who begins a
15
+ * flow, a topic in the middle, a partner's API — each is named here, so the
16
+ * reader decides whether it needs a double, a fixture or a directory. The
17
+ * caller that only wants seeds (`servicesInFlowsClaiming`) reads `services` and
18
+ * ignores the rest, which is a choice it can see itself making.
19
+ *
20
+ * Sorted with compareIds rather than in step order: a union has no document
21
+ * order to preserve, and a payload ordered by LikeC4's record order would
22
+ * reorder under a dependency bump.
23
+ */
24
+ export function participantsOf(scan, views) {
25
+ const known = scan.model.known;
26
+ const services = new Set();
27
+ const unresolved = new Set();
28
+ for (const view of views) {
29
+ for (const step of view.steps) {
30
+ for (const id of [step.source, step.target]) {
31
+ const service = scan.resolve(id);
32
+ if (known.has(service))
33
+ services.add(service);
34
+ else
35
+ unresolved.add(id);
36
+ }
37
+ }
38
+ }
39
+ const byId = new Map(scan.model.elements.map((e) => [e.id, e]));
40
+ return {
41
+ services: [...services].sort(compareIds),
42
+ unresolved: [...unresolved].sort(compareIds).map((element) => ({ element, external: isExternal(element, byId, known) })),
43
+ };
44
+ }
45
+ /**
46
+ * `#external` on the element or on anything it is drawn inside — up to, and
47
+ * including, the first element that stands for a service.
48
+ *
49
+ * The walk is `core/gate/partners.ts`' reading of the same tag (`endFacts`),
50
+ * stop included, so `list suites` and the gate cannot disagree about whether
51
+ * an endpoint is ours. Walking up is what is true for a stand: `stripe.api`
52
+ * drawn inside an `#external` `stripe` is somebody else's endpoint whether or
53
+ * not the container repeats the tag, and LikeC4 does not inherit tags
54
+ * downward, so reading the element's own tags alone would print "ours, with no
55
+ * directory" over a partner's API. Stopping is just as true: an element bound
56
+ * with `metadata { service '<id>' }`, or titled for a real `services/<id>/`,
57
+ * IS a service, and what sits above it is a grouping whose `#external` is the
58
+ * grouping's claim, not the service's. Without the stop a service bound inside
59
+ * an `#external` zone and not yet given a directory was printed as "bring a
60
+ * double" while the gate graded it as ours. The tag on that element itself is
61
+ * read before the stop, as the gate reads it.
62
+ *
63
+ * "Stands for a service" is `commands/validate/fleet/census.ts`'
64
+ * `standsForService`, spelled here rather than imported because core may not
65
+ * reach into commands — as `core/gate/partners.ts` spells it, and as
66
+ * `core/c4/resolve/systems.ts` does for the feature-scope graders.
67
+ *
68
+ * A tag a KIND declares (`element topic { #external }`) arrives on every element
69
+ * of that kind in the parsed model, so a topic counts without repeating it.
70
+ */
71
+ function isExternal(element, byId, known) {
72
+ for (const id of ancestorIds(element)) {
73
+ const e = byId.get(id);
74
+ if (e === undefined)
75
+ continue;
76
+ if (e.tags.includes(EXTERNAL_TAG))
77
+ return true;
78
+ if (e.service !== undefined || known.has(e.title))
79
+ return false;
80
+ }
81
+ return false;
82
+ }
83
+ //# sourceMappingURL=participants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"participants.js","sourceRoot":"","sources":["../../../../src/core/usecases/suites/participants.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA2B5D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc,EAAE,KAA4B;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,KAAK,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACjC,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;oBAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;;oBACzC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO;QACL,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;KACzH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAS,UAAU,CAAC,OAAe,EAAE,IAA+B,EAAE,KAA0B;IAC9F,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,KAAK,SAAS;YAAE,SAAS;QAC9B,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * Who takes part in a set of use cases — the union a test environment is built\n * from, and the participants that union cannot place.\n *\n * It began as `servicesInFlowsClaiming` in `../capability.ts`, which answered\n * the same question for the flows claiming one capability and kept only the\n * half `loam explore` needed: the enumerated services. A suite asks it of a\n * different set of views — the ones carrying one `#suite-` tag — and needs the\n * other half too, so the walk moved here and grew a second output rather than\n * being written twice. Two walks over \"which services does this flow touch\"\n * would be two answers to the question a test stand is provisioned from.\n *\n * THE DEPENDENCY RUNS PARENT → CHILD, never back. `../capability.ts` and\n * `../fleet.ts` (which takes the suite prefix from `./suites.ts`) import this\n * package, so nothing here may value-import `core/usecases/` — the package\n * graph would close a cycle `import/no-cycle` cannot see (docs/DESIGN.md rule\n * 21). `UseCaseScan` crosses as a TYPE only, which erases.\n */\nimport type { Elem } from \"../../c4/likec4.js\";\nimport type { ParsedView } from \"../../c4/parsed/dynamic-views.js\";\nimport { ancestorIds } from \"../../kernel/ids/fqn/ancestors.js\";\nimport { compareIds } from \"../../repo/entries.js\";\nimport { EXTERNAL_TAG } from \"../../vocabulary/maturity.js\";\nimport type { UseCaseScan } from \"../fleet.js\";\n\n/** A scan that read — the only kind with elements to resolve against. */\nexport type ReadScan = Extract<UseCaseScan, { kind: \"read\" }>;\n\n/** A participant the element→service join could not land on an enumerated service. */\nexport interface UnresolvedParticipant {\n /** The element id a step drew, exactly as the view spells it. */\n element: string;\n /**\n * Does the element, or anything it is drawn inside short of a service, carry\n * `#external`? Then it is deliberately not the fleet's, and a stand needs a\n * double for it rather than a directory. See `isExternal` for why an\n * ancestor's tag counts and why the walk stops at a service.\n */\n external: boolean;\n}\n\n/** The union of a set of views' participants, split by whether the fleet can stand each one up. */\nexport interface Participants {\n /** Enumerated service ids, sorted and deduplicated. */\n services: string[];\n /** Everything else the steps drew, one entry per element id, sorted by it. */\n unresolved: UnresolvedParticipant[];\n}\n\n/**\n * Every endpoint the views' steps draw, resolved through the scan's ONE\n * element→service resolver — the resolver `attributeStep` grades the same hops\n * with, built over the same enumerated fleet (`readUseCases`). A second\n * resolver here could not disagree today and would be a second place to change.\n *\n * AN ENDPOINT THAT RESOLVES TO NO ENUMERATED SERVICE IS REPORTED, NEVER DROPPED.\n * This answer is what a test environment is provisioned from, and a silently\n * shorter list is the one failure that cannot be seen from the list itself: the\n * stand comes up, the missing participant is simply absent, and the journey\n * fails against something nobody was told to start. The actor who begins a\n * flow, a topic in the middle, a partner's API — each is named here, so the\n * reader decides whether it needs a double, a fixture or a directory. The\n * caller that only wants seeds (`servicesInFlowsClaiming`) reads `services` and\n * ignores the rest, which is a choice it can see itself making.\n *\n * Sorted with compareIds rather than in step order: a union has no document\n * order to preserve, and a payload ordered by LikeC4's record order would\n * reorder under a dependency bump.\n */\nexport function participantsOf(scan: ReadScan, views: readonly ParsedView[]): Participants {\n const known = scan.model.known;\n const services = new Set<string>();\n const unresolved = new Set<string>();\n for (const view of views) {\n for (const step of view.steps) {\n for (const id of [step.source, step.target]) {\n const service = scan.resolve(id);\n if (known.has(service)) services.add(service);\n else unresolved.add(id);\n }\n }\n }\n const byId = new Map(scan.model.elements.map((e) => [e.id, e]));\n return {\n services: [...services].sort(compareIds),\n unresolved: [...unresolved].sort(compareIds).map((element) => ({ element, external: isExternal(element, byId, known) })),\n };\n}\n\n/**\n * `#external` on the element or on anything it is drawn inside — up to, and\n * including, the first element that stands for a service.\n *\n * The walk is `core/gate/partners.ts`' reading of the same tag (`endFacts`),\n * stop included, so `list suites` and the gate cannot disagree about whether\n * an endpoint is ours. Walking up is what is true for a stand: `stripe.api`\n * drawn inside an `#external` `stripe` is somebody else's endpoint whether or\n * not the container repeats the tag, and LikeC4 does not inherit tags\n * downward, so reading the element's own tags alone would print \"ours, with no\n * directory\" over a partner's API. Stopping is just as true: an element bound\n * with `metadata { service '<id>' }`, or titled for a real `services/<id>/`,\n * IS a service, and what sits above it is a grouping whose `#external` is the\n * grouping's claim, not the service's. Without the stop a service bound inside\n * an `#external` zone and not yet given a directory was printed as \"bring a\n * double\" while the gate graded it as ours. The tag on that element itself is\n * read before the stop, as the gate reads it.\n *\n * \"Stands for a service\" is `commands/validate/fleet/census.ts`'\n * `standsForService`, spelled here rather than imported because core may not\n * reach into commands — as `core/gate/partners.ts` spells it, and as\n * `core/c4/resolve/systems.ts` does for the feature-scope graders.\n *\n * A tag a KIND declares (`element topic { #external }`) arrives on every element\n * of that kind in the parsed model, so a topic counts without repeating it.\n */\nfunction isExternal(element: string, byId: ReadonlyMap<string, Elem>, known: ReadonlySet<string>): boolean {\n for (const id of ancestorIds(element)) {\n const e = byId.get(id);\n if (e === undefined) continue;\n if (e.tags.includes(EXTERNAL_TAG)) return true;\n if (e.service !== undefined || known.has(e.title)) return false;\n }\n return false;\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { compareIds } from "../../repo/entries.js";
2
+ import { participantsOf } from "./participants.js";
3
+ /**
4
+ * The reserved tag prefix, matched EXACTLY — case included — as `cap-` and
5
+ * `req-` are. Exported for `../fleet.ts`, where it joins the other two in the
6
+ * one opt-in predicate and the one byte gate in front of it.
7
+ */
8
+ export const SUITE_TAG_PREFIX = "suite-";
9
+ /**
10
+ * The suite names one view declares: the tags with the prefix stripped,
11
+ * deduplicated and sorted.
12
+ *
13
+ * A bare `#suite-` is a suite named `''` and is KEPT, for the reason a bare
14
+ * `#cap-` stays a claim: the prefix is the opt-in however little follows it,
15
+ * and a view that asked to be in a suite must not silently be in none.
16
+ */
17
+ function suiteNames(view) {
18
+ const names = view.tags.filter((tag) => tag.startsWith(SUITE_TAG_PREFIX)).map((tag) => tag.slice(SUITE_TAG_PREFIX.length));
19
+ return [...new Set(names)].sort(compareIds);
20
+ }
21
+ /**
22
+ * Group the scan's use cases by suite.
23
+ *
24
+ * The order is by (document, view id). `../place.ts`' `viewFile` is how a
25
+ * document is SPELLED, and it only prefixes `sourcePath` with a constant, so
26
+ * ordering on `sourcePath` itself orders identically without pointing this
27
+ * package back at its parent.
28
+ */
29
+ export function suiteListing(scan) {
30
+ const ordered = [...scan.views].sort((a, b) => compareIds(a.sourcePath ?? "", b.sourcePath ?? "") || compareIds(a.id, b.id));
31
+ const byName = new Map();
32
+ const ungrouped = [];
33
+ for (const view of ordered) {
34
+ const names = suiteNames(view);
35
+ if (names.length === 0)
36
+ ungrouped.push(view);
37
+ for (const name of names)
38
+ byName.set(name, [...(byName.get(name) ?? []), view]);
39
+ }
40
+ const suites = [...byName]
41
+ .sort(([a], [b]) => compareIds(a, b))
42
+ .map(([name, views]) => ({ name, views, ...participantsOf(scan, views) }));
43
+ return { suites, ungrouped };
44
+ }
45
+ //# sourceMappingURL=suites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suites.js","sourceRoot":"","sources":["../../../../src/core/usecases/suites/suites.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,cAAc,EAA6C,MAAM,mBAAmB,CAAC;AAE9F;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC;AAEzC;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,IAAiC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3H,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC9C,CAAC;AA6BD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CACvF,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC/C,MAAM,SAAS,GAAiB,EAAE,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC;SACvB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SACpC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * Suites: which use cases run together, and what a run of one needs standing up.\n *\n * A use case joins a suite by carrying a `#suite-<name>` tag on its `dynamic\n * view`. A TAG rather than a directory, because suites are many-to-many — one\n * journey belongs to the smoke suite and the payments suite at once — and a\n * directory gives exactly one group. The tag also buys the typo check for free:\n * LikeC4 refuses an undeclared tag, so a suite declared once in the landscape's\n * `specification` block makes a mistyped suite a parse error, where a mistyped\n * directory would silently open a second, empty suite.\n *\n * A SEPARATE PREFIX, not `#cap-`. A capability says what a flow is FOR and may\n * be claimed once per meaning; a suite says what a flow is RUN WITH, and a\n * flow is routinely in several. Reusing the capability tag would make every\n * suite a business promise and every smoke run a capability, and the `#cap-`\n * resolution ladder (`../../capabilities/usecase-join.ts`) would convict every\n * suite name the vocabulary does not declare. The prefix also keeps the tag\n * clear of the feature-id grammar: a view tag is never read as a `#FEAT-`\n * marker (feature tags are read off elements and relationships only), and no\n * other reserved prefix — `cap-`, `req-`, `obl-` — is a prefix of this one or\n * has it as a prefix, so no tag can be claimed by two readers.\n *\n * TAGGING IS THE OPT-IN, and that is the owner's call recorded in SCHEMA.md: a\n * view carrying only `#suite-` tags IS a use case — `../fleet.ts`' `isUseCase`\n * admits it — and is graded hop by hop like any other. Nothing here grades; this\n * module groups the views the scan already admitted.\n *\n * Depends on its parent by TYPE only — `./participants.ts` says why the value\n * direction is parent → child.\n */\nimport type { ParsedView } from \"../../c4/parsed/dynamic-views.js\";\nimport { compareIds } from \"../../repo/entries.js\";\nimport { participantsOf, type ReadScan, type UnresolvedParticipant } from \"./participants.js\";\n\n/**\n * The reserved tag prefix, matched EXACTLY — case included — as `cap-` and\n * `req-` are. Exported for `../fleet.ts`, where it joins the other two in the\n * one opt-in predicate and the one byte gate in front of it.\n */\nexport const SUITE_TAG_PREFIX = \"suite-\";\n\n/**\n * The suite names one view declares: the tags with the prefix stripped,\n * deduplicated and sorted.\n *\n * A bare `#suite-` is a suite named `''` and is KEPT, for the reason a bare\n * `#cap-` stays a claim: the prefix is the opt-in however little follows it,\n * and a view that asked to be in a suite must not silently be in none.\n */\nfunction suiteNames(view: { tags: readonly string[] }): string[] {\n const names = view.tags.filter((tag) => tag.startsWith(SUITE_TAG_PREFIX)).map((tag) => tag.slice(SUITE_TAG_PREFIX.length));\n return [...new Set(names)].sort(compareIds);\n}\n\n/** One suite: its name, its use cases, and the environment their union needs. */\nexport interface Suite {\n /** The tag without `#suite-` — what `loam list suites <name>` takes. */\n name: string;\n /** The use cases carrying the tag, ordered by (document, view id). */\n views: ParsedView[];\n /** The enumerated services those use cases draw — what must be up. */\n services: string[];\n /** Participants that resolve to no enumerated service — named, never dropped. */\n unresolved: UnresolvedParticipant[];\n}\n\n/** Every suite the fleet's use cases declare, and the use cases in none. */\nexport interface SuiteListing {\n /** Sorted by name. */\n suites: Suite[];\n /**\n * The use cases carrying no `#suite-` tag, ordered by (document, view id).\n *\n * Answered because everything else here is per-suite: a journey in no suite\n * is absent from every suite's list, which is the one absence a per-suite\n * output cannot show. Legal and normal — an unfiled service is the same shape\n * — so it is named and never graded.\n */\n ungrouped: ParsedView[];\n}\n\n/**\n * Group the scan's use cases by suite.\n *\n * The order is by (document, view id). `../place.ts`' `viewFile` is how a\n * document is SPELLED, and it only prefixes `sourcePath` with a constant, so\n * ordering on `sourcePath` itself orders identically without pointing this\n * package back at its parent.\n */\nexport function suiteListing(scan: ReadScan): SuiteListing {\n const ordered = [...scan.views].sort(\n (a, b) => compareIds(a.sourcePath ?? \"\", b.sourcePath ?? \"\") || compareIds(a.id, b.id),\n );\n const byName = new Map<string, ParsedView[]>();\n const ungrouped: ParsedView[] = [];\n for (const view of ordered) {\n const names = suiteNames(view);\n if (names.length === 0) ungrouped.push(view);\n for (const name of names) byName.set(name, [...(byName.get(name) ?? []), view]);\n }\n const suites = [...byName]\n .sort(([a], [b]) => compareIds(a, b))\n .map(([name, views]) => ({ name, views, ...participantsOf(scan, views) }));\n return { suites, ungrouped };\n}\n"]}
@@ -36,6 +36,17 @@ export const VERDICTS = ["confirmed", "unconfirmed"];
36
36
  * `attestedClaims` in `./record.ts` — exactly where it was.
37
37
  */
38
38
  export const ANSWERED_BY = ["runner", "external-runner", "agent"];
39
+ /**
40
+ * The entries an answers file holds — a bare array, or an object with an
41
+ * `answers` array — or null for any other shape. One reading of the file's
42
+ * shape for `checkAnswers` and `--diff-answers`' off-lens note, so the two
43
+ * cannot disagree about which entries a file has.
44
+ */
45
+ export function answerEntries(raw) {
46
+ if (Array.isArray(raw))
47
+ return raw;
48
+ return isRecord(raw) && Array.isArray(raw["answers"]) ? raw["answers"] : null;
49
+ }
39
50
  /**
40
51
  * Accept an answer set only if it answers THIS checklist: every claim once, no
41
52
  * claim twice, nothing extra, and no "confirmed" that shows nothing.
@@ -66,11 +77,7 @@ export const ANSWERED_BY = ["runner", "external-runner", "agent"];
66
77
  */
67
78
  export function checkAnswers(claims, raw, runnerOwned, context) {
68
79
  const refuse = (code, message) => ({ ok: false, code, message });
69
- const list = Array.isArray(raw)
70
- ? raw
71
- : isRecord(raw) && Array.isArray(raw["answers"])
72
- ? raw["answers"]
73
- : null;
80
+ const list = answerEntries(raw);
74
81
  if (list === null) {
75
82
  return refuse("answers-unreadable", "Expected a JSON array of answers, or an object with an `answers` array: " +
76
83
  `[{ "id": "...", "verdict": "confirmed", "evidence": ["file:line"] }]`);