@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,290 @@
1
+ /**
2
+ * Which declared relationship backs one hop of a use case, and what it says the
3
+ * operation is.
4
+ *
5
+ * A `dynamic view`'s hop says "web talks to orders". Answering it is the same
6
+ * shape of join `Covers:` performs for an edge entry one module over — same two
7
+ * tiers, same cached `serviceResolver` (`./service.ts`'s `resolverFor`, which
8
+ * both axes share), same "an endpoint names an element or the service it stands
9
+ * for" rule.
10
+ *
11
+ * It lived in `../arch.ts` while that file was the only home the join had, and
12
+ * moved here when the Covers grammar grew a fourth entry form and the file
13
+ * reached its line limit. The move changed nothing: the seam was already named
14
+ * in that file's own header, and this is the package the resolution it depends
15
+ * on lives in.
16
+ *
17
+ * It does NOT live beside the view reader in `../parsed/dynamic-views.ts`, and
18
+ * that is a rule rather than a preference: a second copy of this join is the
19
+ * copy that drifts, and that module may read `$data` and nothing else
20
+ * (docs/DESIGN.md rule 26) — it has no business holding the model join.
21
+ */
22
+ import {} from "../likec4.js";
23
+ import { resolverFor } from "./service.js";
24
+ /**
25
+ * The pair a step's backing relationship is looked up under. This is the half
26
+ * that mis-grades the whole fleet if it is wrong, because a reply arrow is the
27
+ * commonest step in any sequence diagram.
28
+ *
29
+ * MEASURED at the `likec4@1.59.2` pin, and `test/likec4-view-shape.test.ts` holds
30
+ * the raw form: a reply written `a <- b 'reply'` parses as
31
+ * `{source: "b", target: "a", isBackward: true}` — LikeC4 has ALREADY reversed
32
+ * the endpoints and set the flag — while the forward `b -> a` yields that same
33
+ * pair with no `isBackward` key at all (ABSENT, not false). So the endpoints as
34
+ * parsed describe the direction the MESSAGE travels, and the flag records which
35
+ * arrow the author typed.
36
+ *
37
+ * loam attributes a reply to the CALL it answers, so the reversal is undone
38
+ * here. A landscape declares `web -> orders` because that edge is the
39
+ * dependency; a mirror `orders -> web` for each response is not something a
40
+ * fleet writes down. Reading a reply hop under the message's own direction would
41
+ * report every return hop against such a landscape as `unbacked`, and where a
42
+ * fleet does draw the mirror edge it would file the two halves of one call under
43
+ * two operations — the call under `op 'createOrder'`, its reply under none.
44
+ *
45
+ * That is deliberately NOT what LikeC4's own computed stage does, and the
46
+ * difference is measured rather than assumed: that stage resolves a backward
47
+ * step's `relations` from the UNFLIPPED pair, so at this pin a `web <- orders`
48
+ * step computes to `relations: []` when only `web -> orders` exists, and to the
49
+ * mirror edge when only `orders -> web` does. It is answering which arrow to
50
+ * draw; loam is answering which operation the hop exercises, and reads what a
51
+ * view DECLARES rather than what it shows — which is why loam never reaches that
52
+ * stage at all (docs/DESIGN.md rule 26, enforced by `scripts/arch-check.mjs`).
53
+ * The consequence to know before changing this: where
54
+ * a fleet declares BOTH directions, the mirror edge is not what backs the reply
55
+ * here — the call is, and the mirror edge backs only a hop drawn forward along
56
+ * it.
57
+ */
58
+ function callPair(step) {
59
+ return step.isBackward ? { from: step.target, to: step.source } : { from: step.source, to: step.target };
60
+ }
61
+ /**
62
+ * The distinct `op` values across candidates, first-seen order preserved.
63
+ *
64
+ * Absent is a VALUE here, not a gap to skip: a relationship carrying no `op` and
65
+ * one carrying `createOrder` disagree about the operation just as surely as two
66
+ * different ops do, and skipping the absent one would silently promote that
67
+ * disagreement to a confident answer. `includes` rather than a `Set` because a
68
+ * candidate list is the handful of relationships between one pair of endpoints,
69
+ * and it keeps `undefined` comparing the way the rest of this file expects.
70
+ */
71
+ function distinctOps(rels) {
72
+ const out = [];
73
+ for (const r of rels)
74
+ if (!out.includes(r.op))
75
+ out.push(r.op);
76
+ return out;
77
+ }
78
+ /**
79
+ * What a relationship can be picked by: the identifiers it DECLARES — its
80
+ * `metadata { op }`, and the message it `publishes` or `consumes` — and never
81
+ * its title, which is prose. An empty value names nothing: `op ''` parses, and
82
+ * every title starts with the empty string.
83
+ */
84
+ function declaredNames(rel) {
85
+ return [rel.op, rel.publishes, rel.consumes].filter((name) => name !== undefined && name !== "");
86
+ }
87
+ /**
88
+ * The characters an operation id or a message name is spelled with. A name
89
+ * spelled with these alone ends where the next character is not one of them,
90
+ * so of two such names where one prefixes the other (`order.placed` and
91
+ * `order.placed.v2`, `createOrder` and `createOrder-batch`) one title never
92
+ * picks both. A name spelled with anything else (`orders/create`) still
93
+ * matches, and where it shares a prefix with another candidate's (`orders`) a
94
+ * title opening with the longer one picks both and the hop stays contested —
95
+ * which fails closed rather than guessing.
96
+ */
97
+ const NAME_CHARACTER = /[A-Za-z0-9_.-]/;
98
+ /** A letter or a digit in any script — what a word of prose starts with. */
99
+ const WORD_CHARACTER = /[\p{L}\p{N}]/u;
100
+ /**
101
+ * Does `title` open with `name`, set off as a name rather than as the first
102
+ * word of a sentence?
103
+ *
104
+ * Two conditions after the name. The next character must not continue it, so
105
+ * `createOrderBatch` is not `createOrder`. And the title must end there, or go
106
+ * on with punctuation — `:`, `(`, `,`, a dash — before any further word:
107
+ * `'createOrder — places the order'`, `'getOrder(orderId)'` and a bare
108
+ * `'refundPayment'` name an operation, while `'get the customer's orders'`
109
+ * does not name `get`. The second condition is what keeps a fleet whose
110
+ * operationIds are plain verbs from having its existing prose titles read as
111
+ * picks on the day it upgrades: the first word of a sentence is not an author
112
+ * naming an operation.
113
+ */
114
+ function opensWith(title, name) {
115
+ if (!title.startsWith(name))
116
+ return false;
117
+ const rest = title.slice(name.length);
118
+ if (rest === "")
119
+ return true;
120
+ if (NAME_CHARACTER.test(rest.charAt(0)))
121
+ return false;
122
+ return !WORD_CHARACTER.test(rest.trimStart().charAt(0));
123
+ }
124
+ /**
125
+ * The candidates a step's title picks: those that declare the name the title
126
+ * opens with (`opensWith`). `[]` when the step has no title or its title opens
127
+ * with no candidate's name.
128
+ *
129
+ * This is how an author says which relationship a hop means when the map joins
130
+ * the pair by several — `orders -> payments 'authorizePayment — authorizes the
131
+ * payment'` against an `authorizePayment` edge and a `refundPayment` one. It
132
+ * exists because every other way to say it is closed, and each was MEASURED at
133
+ * the 1.59.2 pin before this was chosen:
134
+ *
135
+ * - `metadata { op }` on a step is a parse error (the step accepts title,
136
+ * `technology`, `description`, `notes`, `navigateTo` and the styling
137
+ * properties, and no metadata block), and so are tags on a step;
138
+ * - a step's KIND exists only as the arrow `-[kind]->`, which needs a
139
+ * `relationship <kind>` in the specification and names a TRANSPORT — the
140
+ * reserve, read and release calls one caller makes to one provider are one
141
+ * kind, so a kind cannot tell them apart;
142
+ * - the repair the warning used to offer alone, drawing the hop between the
143
+ * elements that carry exactly one candidate, is closed on a fleet flow
144
+ * wherever those elements are containers an extending model declares: a
145
+ * hop naming one does not resolve in the `architecture/` project at all
146
+ * (SCHEMA.md, "What loam grades is still the `architecture/` project
147
+ * alone"), so between two services drawn as single elements on the map
148
+ * there was no repair short of changing the map.
149
+ *
150
+ * The title is what is left, and it is the native place: a sequence diagram
151
+ * labels a message with the operation it invokes. The rule is deliberately
152
+ * narrow, because a guessed operation reads exactly like a right one in every
153
+ * rollup built over a flow:
154
+ *
155
+ * - the title must START with the name, set off from any prose after it by
156
+ * punctuation — a candidate's name later in the sentence ("reads the order
157
+ * before release") or as its first word ("read the order") picks nothing,
158
+ * since a word in a sentence is not an author naming an operation;
159
+ * - it must be a candidate's DECLARED identifier, matched exactly, case
160
+ * included — an operationId is case-sensitive, and a relationship's title is
161
+ * prose nobody wrote to be matched;
162
+ * - it only ever chooses AMONG the candidates the endpoints found, and only
163
+ * when they disagree: a title can never back an unbacked hop, move a hop to
164
+ * another tier, or overrule candidates that already agree.
165
+ */
166
+ function pickedByTitle(rels, title) {
167
+ if (title === undefined)
168
+ return [];
169
+ return rels.filter((rel) => declaredNames(rel).some((name) => opensWith(title, name)));
170
+ }
171
+ /**
172
+ * A title that WOULD settle a disagreement among `rels` — the step's own
173
+ * `title` behind one candidate's declared name — or `undefined` when no single
174
+ * name does.
175
+ *
176
+ * For the `usecase.step-contested` message, whose example is only worth giving
177
+ * if pasting it clears the warning. So it is tried through `pickedByTitle`
178
+ * itself rather than read off a candidate: a name two disagreeing candidates
179
+ * share, or one the title already opens with to no effect, is skipped instead
180
+ * of offered. Operation ids are tried before message names, because the
181
+ * warning is about operations and the hop is more often a call.
182
+ */
183
+ export function pickingTitle(rels, title) {
184
+ const names = [...rels.map((rel) => rel.op), ...rels.flatMap((rel) => [rel.publishes, rel.consumes])];
185
+ for (const name of names) {
186
+ if (name === undefined || name === "")
187
+ continue;
188
+ const spelled = title === undefined ? name : `${name} — ${title}`;
189
+ if (distinctOps(pickedByTitle(rels, spelled)).length === 1)
190
+ return spelled;
191
+ }
192
+ return undefined;
193
+ }
194
+ /** The `attributed` verdict over candidates already known to agree on `op`. */
195
+ function attributed(pair, tier, rels, op) {
196
+ return { ...pair, verdict: "attributed", tier, rels, ...(op === undefined ? {} : { op }) };
197
+ }
198
+ /**
199
+ * The verdict one tier's candidates imply, or `undefined` when that tier found
200
+ * nothing and is therefore not the tier that answers.
201
+ *
202
+ * The count that decides is the DISTINCT-OP count, never the candidate count.
203
+ * That distinction is what makes the service tier safe: once a service is drawn
204
+ * as containers, `a.api -> b` and `a.worker -> b` both carrying
205
+ * `op 'createOrder'` are two relationships and one operation, and a verdict read
206
+ * off `rels.length` would convict a perfectly consistent model of contesting
207
+ * itself. The same count decides the title's pick: a title that picks two
208
+ * candidates still disagreeing — one name declared as one edge's `op` and
209
+ * another's `publishes` — has said nothing that settles the hop, and the
210
+ * verdict stays `contested` over every candidate rather than over the two.
211
+ */
212
+ function verdictOn(pair, tier, rels, title) {
213
+ if (rels.length === 0)
214
+ return undefined;
215
+ const ops = distinctOps(rels);
216
+ if (ops.length > 1) {
217
+ const picked = pickedByTitle(rels, title);
218
+ const pickedOps = distinctOps(picked);
219
+ if (pickedOps.length === 1)
220
+ return attributed(pair, tier, picked, pickedOps[0]);
221
+ return { ...pair, verdict: "contested", tier, rels, ops };
222
+ }
223
+ // Length is exactly one here, so the single distinct value is the answer —
224
+ // and `undefined` is a legitimate answer: every candidate agreed that this
225
+ // hop names no operation.
226
+ return attributed(pair, tier, rels, ops[0]);
227
+ }
228
+ /**
229
+ * Relationships whose ENDPOINTS resolve to the same two services as the pair —
230
+ * the fallback tier, and the reason a step drawn between two services matches an
231
+ * edge drawn between two containers.
232
+ *
233
+ * EMPTY when both endpoints resolve to ONE service, and that is a correction
234
+ * rather than an optimisation. The service tier exists for a step drawn between
235
+ * two SERVICES, where "which edge backs it" is answered by collapsing each
236
+ * side's containers to the service that owns them. Inside one service there is
237
+ * no service-tier reading: collapsing both sides yields the pair
238
+ * (svc, svc), which EVERY internal relationship of that service matches —
239
+ * measured at the 1.59.2 pin on a bound service drawn as three containers:
240
+ * a hop `api -> db` with no declared edge and two unrelated internal edges
241
+ * (`api -> workflow`, `workflow -> db`) came back `attributed` on tier 2 with
242
+ * both edges as candidates, all agreeing on no `op`. So an unbacked hop between
243
+ * two containers of one service could never earn `usecase.step-unbacked` — the
244
+ * one grade this join exists to produce — until the moment a fleet drew a
245
+ * service's flow over its own containers (`core/usecases/service/flows.ts`),
246
+ * and a landscape that draws a service as containers had the same wrong answer
247
+ * for the same hop all along.
248
+ *
249
+ * Through the module's cached `resolverFor`, not a fresh `serviceResolver`: this
250
+ * runs once per step of every use case in the fleet, and building the id map per
251
+ * step is the cost that comment above `RESOLVERS` exists to prevent.
252
+ */
253
+ function resolvedTier(pair, scope) {
254
+ const resolve = resolverFor(scope.elements, scope.known);
255
+ const from = resolve(pair.from);
256
+ const to = resolve(pair.to);
257
+ if (from === to)
258
+ return [];
259
+ return scope.relationships.filter((r) => resolve(r.source) === from && resolve(r.target) === to);
260
+ }
261
+ /**
262
+ * Which relationship in the model backs one hop of a use case, and what it says
263
+ * the operation is.
264
+ *
265
+ * Two tiers, and the second runs ONLY when the first is empty. That ordering is
266
+ * load-bearing rather than an optimisation: the service tier necessarily
267
+ * re-finds every exact match — an endpoint that matches literally resolves to
268
+ * the same service trivially — so running it unconditionally would let a
269
+ * container-level edge with a different `op` contest a step the model already
270
+ * answers exactly, turning an `attributed` verdict into a `contested` one on a
271
+ * document nobody changed. The step's title is read inside a tier, never across
272
+ * them: it narrows the tier that answered and cannot reach the other.
273
+ *
274
+ * Relationship `kind` is deliberately not part of the match, and that is
275
+ * measured too: at this pin `a -[http]-> b` without a `relationship http` in the
276
+ * `specification` block is "Could not resolve reference to RelationshipKind
277
+ * named 'http'" — a parse error, which under loam's standing rule means no model
278
+ * at all — and `test/helpers/harness.ts`'s own `LANDSCAPE` declares no
279
+ * relationship kinds. So narrowing by kind would mean a fix message asking an
280
+ * author to write a line their document does not parse.
281
+ */
282
+ export function attributeStep(step, scope) {
283
+ const pair = callPair(step);
284
+ const exact = scope.relationships.filter((r) => r.source === pair.from && r.target === pair.to);
285
+ const declared = verdictOn(pair, 1, exact, step.title);
286
+ if (declared !== undefined)
287
+ return declared;
288
+ return verdictOn(pair, 2, resolvedTier(pair, scope), step.title) ?? { ...pair, verdict: "unbacked" };
289
+ }
290
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../../../src/core/c4/resolve/steps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which declared relationship backs one hop of a use case, and what it says the\n * operation is.\n *\n * A `dynamic view`'s hop says \"web talks to orders\". Answering it is the same\n * shape of join `Covers:` performs for an edge entry one module over — same two\n * tiers, same cached `serviceResolver` (`./service.ts`'s `resolverFor`, which\n * both axes share), same \"an endpoint names an element or the service it stands\n * for\" rule.\n *\n * It lived in `../arch.ts` while that file was the only home the join had, and\n * moved here when the Covers grammar grew a fourth entry form and the file\n * reached its line limit. The move changed nothing: the seam was already named\n * in that file's own header, and this is the package the resolution it depends\n * on lives in.\n *\n * It does NOT live beside the view reader in `../parsed/dynamic-views.ts`, and\n * that is a rule rather than a preference: a second copy of this join is the\n * copy that drifts, and that module may read `$data` and nothing else\n * (docs/DESIGN.md rule 26) — it has no business holding the model join.\n */\nimport { type Elem, type Rel } from \"../likec4.js\";\nimport { resolverFor } from \"./service.js\";\nimport type { ParsedStep } from \"../parsed/dynamic-views.js\";\n\n/**\n * The fields an attribution reads off a step, and no more: the endpoints, and\n * the title that may choose between the candidates the endpoints found.\n *\n * Narrower than `ParsedStep` on purpose. Attribution must not depend on the\n * ordinal or the notes, and the title never decides WHETHER a hop is backed —\n * a hop is backed by where it points, never by what the author called it. The\n * title has exactly one job, `pickedByTitle`'s: when the relationships the\n * endpoints found disagree about the operation, a title that opens with one\n * candidate's own declared name narrows them to the ones this hop means. A\n * signature that says so is also a signature a test can satisfy with a literal\n * instead of a parsed document. Derived from `ParsedStep` rather than restated,\n * so a rename upstream is a compile error here instead of a second definition\n * that quietly disagrees.\n *\n * `title` is optional because an untitled step is legal, and that makes it the\n * one field a hand-built literal can leave out and still compile — silently\n * losing the pick. Both callers pass a `ParsedStep`, which carries it.\n */\nexport type StepReading = Pick<ParsedStep, \"source\" | \"target\" | \"isBackward\" | \"title\">;\n\n/**\n * What a step is attributed against: the model in view, plus the enumerated\n * fleet where the caller has it.\n *\n * A deliberate subset of `CoverageScope` rather than a reuse of it — an\n * attribution never reads `health`, and demanding one would make every caller\n * fabricate an empty `HealthIds` to ask a question that has nothing to do with\n * alerts. A `CoverageScope` is still structurally assignable here, so a caller\n * already holding the fuller scope passes it through unchanged.\n */\nexport interface StepScope {\n elements: Elem[];\n relationships: Rel[];\n /** As `CoverageScope.known`, for the same reason: one set per run, shared. */\n known?: ReadonlySet<string>;\n}\n\n/** The oriented pair a verdict is about — see `callPair` for which way round. */\ninterface CallPair {\n from: string;\n to: string;\n}\n\n/**\n * What the model says about one hop of a use case.\n *\n * Three variants rather than one record with optional fields, because the fields\n * are not independent: `op` is meaningful only when exactly one operation was\n * implied, `ops` only when more than one was, and an unbacked step has neither\n * and no tier either. Tagged, those states stop being constructible.\n *\n * - `attributed` — the candidates on the matched tier agree, so the hop\n * exercises exactly one operation, or they disagree and the step's title\n * picked the ones it means (`pickedByTitle`), in which case `rels` holds the\n * picked candidates only. `op` is absent when that one operation is *no*\n * operation: the relationships back the hop but none of them carries\n * `metadata { op '…' }`.\n * - `contested` — the candidates disagree and the title picks none of them, so\n * loam cannot say which operation the hop exercises. `ops` lists the distinct\n * values in candidate order, `undefined` standing for the candidate that\n * declared none.\n * - `unbacked` — nothing in the model backs the hop on either tier. The pair is\n * still carried, because a message about it has to be able to name the edge\n * the author would have to draw.\n *\n * `rels` carries every candidate on the matched tier, not just the first: an\n * `attributed` verdict over two relationships is the normal shape once a service\n * is drawn as containers, and a caller pinning evidence needs all of them. The\n * one exception is an `attributed` verdict the title picked, whose `rels` holds\n * the picked candidates only: the others are what the author said the hop is\n * NOT, and a rollup over `rels` — a message census, the removal victims in\n * `core/usecases/operations.ts` — must not count them as exercised.\n */\nexport type StepAttribution =\n | (CallPair & { verdict: \"attributed\"; tier: 1 | 2; rels: readonly Rel[]; op?: string })\n | (CallPair & { verdict: \"contested\"; tier: 1 | 2; rels: readonly Rel[]; ops: readonly (string | undefined)[] })\n | (CallPair & { verdict: \"unbacked\" });\n\n/**\n * The pair a step's backing relationship is looked up under. This is the half\n * that mis-grades the whole fleet if it is wrong, because a reply arrow is the\n * commonest step in any sequence diagram.\n *\n * MEASURED at the `likec4@1.59.2` pin, and `test/likec4-view-shape.test.ts` holds\n * the raw form: a reply written `a <- b 'reply'` parses as\n * `{source: \"b\", target: \"a\", isBackward: true}` — LikeC4 has ALREADY reversed\n * the endpoints and set the flag — while the forward `b -> a` yields that same\n * pair with no `isBackward` key at all (ABSENT, not false). So the endpoints as\n * parsed describe the direction the MESSAGE travels, and the flag records which\n * arrow the author typed.\n *\n * loam attributes a reply to the CALL it answers, so the reversal is undone\n * here. A landscape declares `web -> orders` because that edge is the\n * dependency; a mirror `orders -> web` for each response is not something a\n * fleet writes down. Reading a reply hop under the message's own direction would\n * report every return hop against such a landscape as `unbacked`, and where a\n * fleet does draw the mirror edge it would file the two halves of one call under\n * two operations — the call under `op 'createOrder'`, its reply under none.\n *\n * That is deliberately NOT what LikeC4's own computed stage does, and the\n * difference is measured rather than assumed: that stage resolves a backward\n * step's `relations` from the UNFLIPPED pair, so at this pin a `web <- orders`\n * step computes to `relations: []` when only `web -> orders` exists, and to the\n * mirror edge when only `orders -> web` does. It is answering which arrow to\n * draw; loam is answering which operation the hop exercises, and reads what a\n * view DECLARES rather than what it shows — which is why loam never reaches that\n * stage at all (docs/DESIGN.md rule 26, enforced by `scripts/arch-check.mjs`).\n * The consequence to know before changing this: where\n * a fleet declares BOTH directions, the mirror edge is not what backs the reply\n * here — the call is, and the mirror edge backs only a hop drawn forward along\n * it.\n */\nfunction callPair(step: StepReading): CallPair {\n return step.isBackward ? { from: step.target, to: step.source } : { from: step.source, to: step.target };\n}\n\n/**\n * The distinct `op` values across candidates, first-seen order preserved.\n *\n * Absent is a VALUE here, not a gap to skip: a relationship carrying no `op` and\n * one carrying `createOrder` disagree about the operation just as surely as two\n * different ops do, and skipping the absent one would silently promote that\n * disagreement to a confident answer. `includes` rather than a `Set` because a\n * candidate list is the handful of relationships between one pair of endpoints,\n * and it keeps `undefined` comparing the way the rest of this file expects.\n */\nfunction distinctOps(rels: readonly Rel[]): (string | undefined)[] {\n const out: (string | undefined)[] = [];\n for (const r of rels) if (!out.includes(r.op)) out.push(r.op);\n return out;\n}\n\n/**\n * What a relationship can be picked by: the identifiers it DECLARES — its\n * `metadata { op }`, and the message it `publishes` or `consumes` — and never\n * its title, which is prose. An empty value names nothing: `op ''` parses, and\n * every title starts with the empty string.\n */\nfunction declaredNames(rel: Rel): string[] {\n return [rel.op, rel.publishes, rel.consumes].filter((name): name is string => name !== undefined && name !== \"\");\n}\n\n/**\n * The characters an operation id or a message name is spelled with. A name\n * spelled with these alone ends where the next character is not one of them,\n * so of two such names where one prefixes the other (`order.placed` and\n * `order.placed.v2`, `createOrder` and `createOrder-batch`) one title never\n * picks both. A name spelled with anything else (`orders/create`) still\n * matches, and where it shares a prefix with another candidate's (`orders`) a\n * title opening with the longer one picks both and the hop stays contested —\n * which fails closed rather than guessing.\n */\nconst NAME_CHARACTER = /[A-Za-z0-9_.-]/;\n\n/** A letter or a digit in any script — what a word of prose starts with. */\nconst WORD_CHARACTER = /[\\p{L}\\p{N}]/u;\n\n/**\n * Does `title` open with `name`, set off as a name rather than as the first\n * word of a sentence?\n *\n * Two conditions after the name. The next character must not continue it, so\n * `createOrderBatch` is not `createOrder`. And the title must end there, or go\n * on with punctuation — `:`, `(`, `,`, a dash — before any further word:\n * `'createOrder — places the order'`, `'getOrder(orderId)'` and a bare\n * `'refundPayment'` name an operation, while `'get the customer's orders'`\n * does not name `get`. The second condition is what keeps a fleet whose\n * operationIds are plain verbs from having its existing prose titles read as\n * picks on the day it upgrades: the first word of a sentence is not an author\n * naming an operation.\n */\nfunction opensWith(title: string, name: string): boolean {\n if (!title.startsWith(name)) return false;\n const rest = title.slice(name.length);\n if (rest === \"\") return true;\n if (NAME_CHARACTER.test(rest.charAt(0))) return false;\n return !WORD_CHARACTER.test(rest.trimStart().charAt(0));\n}\n\n/**\n * The candidates a step's title picks: those that declare the name the title\n * opens with (`opensWith`). `[]` when the step has no title or its title opens\n * with no candidate's name.\n *\n * This is how an author says which relationship a hop means when the map joins\n * the pair by several — `orders -> payments 'authorizePayment — authorizes the\n * payment'` against an `authorizePayment` edge and a `refundPayment` one. It\n * exists because every other way to say it is closed, and each was MEASURED at\n * the 1.59.2 pin before this was chosen:\n *\n * - `metadata { op }` on a step is a parse error (the step accepts title,\n * `technology`, `description`, `notes`, `navigateTo` and the styling\n * properties, and no metadata block), and so are tags on a step;\n * - a step's KIND exists only as the arrow `-[kind]->`, which needs a\n * `relationship <kind>` in the specification and names a TRANSPORT — the\n * reserve, read and release calls one caller makes to one provider are one\n * kind, so a kind cannot tell them apart;\n * - the repair the warning used to offer alone, drawing the hop between the\n * elements that carry exactly one candidate, is closed on a fleet flow\n * wherever those elements are containers an extending model declares: a\n * hop naming one does not resolve in the `architecture/` project at all\n * (SCHEMA.md, \"What loam grades is still the `architecture/` project\n * alone\"), so between two services drawn as single elements on the map\n * there was no repair short of changing the map.\n *\n * The title is what is left, and it is the native place: a sequence diagram\n * labels a message with the operation it invokes. The rule is deliberately\n * narrow, because a guessed operation reads exactly like a right one in every\n * rollup built over a flow:\n *\n * - the title must START with the name, set off from any prose after it by\n * punctuation — a candidate's name later in the sentence (\"reads the order\n * before release\") or as its first word (\"read the order\") picks nothing,\n * since a word in a sentence is not an author naming an operation;\n * - it must be a candidate's DECLARED identifier, matched exactly, case\n * included — an operationId is case-sensitive, and a relationship's title is\n * prose nobody wrote to be matched;\n * - it only ever chooses AMONG the candidates the endpoints found, and only\n * when they disagree: a title can never back an unbacked hop, move a hop to\n * another tier, or overrule candidates that already agree.\n */\nfunction pickedByTitle(rels: readonly Rel[], title: string | undefined): Rel[] {\n if (title === undefined) return [];\n return rels.filter((rel) => declaredNames(rel).some((name) => opensWith(title, name)));\n}\n\n/**\n * A title that WOULD settle a disagreement among `rels` — the step's own\n * `title` behind one candidate's declared name — or `undefined` when no single\n * name does.\n *\n * For the `usecase.step-contested` message, whose example is only worth giving\n * if pasting it clears the warning. So it is tried through `pickedByTitle`\n * itself rather than read off a candidate: a name two disagreeing candidates\n * share, or one the title already opens with to no effect, is skipped instead\n * of offered. Operation ids are tried before message names, because the\n * warning is about operations and the hop is more often a call.\n */\nexport function pickingTitle(rels: readonly Rel[], title: string | undefined): string | undefined {\n const names = [...rels.map((rel) => rel.op), ...rels.flatMap((rel) => [rel.publishes, rel.consumes])];\n for (const name of names) {\n if (name === undefined || name === \"\") continue;\n const spelled = title === undefined ? name : `${name} — ${title}`;\n if (distinctOps(pickedByTitle(rels, spelled)).length === 1) return spelled;\n }\n return undefined;\n}\n\n/** The `attributed` verdict over candidates already known to agree on `op`. */\nfunction attributed(pair: CallPair, tier: 1 | 2, rels: readonly Rel[], op: string | undefined): StepAttribution {\n return { ...pair, verdict: \"attributed\", tier, rels, ...(op === undefined ? {} : { op }) };\n}\n\n/**\n * The verdict one tier's candidates imply, or `undefined` when that tier found\n * nothing and is therefore not the tier that answers.\n *\n * The count that decides is the DISTINCT-OP count, never the candidate count.\n * That distinction is what makes the service tier safe: once a service is drawn\n * as containers, `a.api -> b` and `a.worker -> b` both carrying\n * `op 'createOrder'` are two relationships and one operation, and a verdict read\n * off `rels.length` would convict a perfectly consistent model of contesting\n * itself. The same count decides the title's pick: a title that picks two\n * candidates still disagreeing — one name declared as one edge's `op` and\n * another's `publishes` — has said nothing that settles the hop, and the\n * verdict stays `contested` over every candidate rather than over the two.\n */\nfunction verdictOn(\n pair: CallPair,\n tier: 1 | 2,\n rels: readonly Rel[],\n title: string | undefined,\n): StepAttribution | undefined {\n if (rels.length === 0) return undefined;\n const ops = distinctOps(rels);\n if (ops.length > 1) {\n const picked = pickedByTitle(rels, title);\n const pickedOps = distinctOps(picked);\n if (pickedOps.length === 1) return attributed(pair, tier, picked, pickedOps[0]);\n return { ...pair, verdict: \"contested\", tier, rels, ops };\n }\n // Length is exactly one here, so the single distinct value is the answer —\n // and `undefined` is a legitimate answer: every candidate agreed that this\n // hop names no operation.\n return attributed(pair, tier, rels, ops[0]);\n}\n\n/**\n * Relationships whose ENDPOINTS resolve to the same two services as the pair —\n * the fallback tier, and the reason a step drawn between two services matches an\n * edge drawn between two containers.\n *\n * EMPTY when both endpoints resolve to ONE service, and that is a correction\n * rather than an optimisation. The service tier exists for a step drawn between\n * two SERVICES, where \"which edge backs it\" is answered by collapsing each\n * side's containers to the service that owns them. Inside one service there is\n * no service-tier reading: collapsing both sides yields the pair\n * (svc, svc), which EVERY internal relationship of that service matches —\n * measured at the 1.59.2 pin on a bound service drawn as three containers:\n * a hop `api -> db` with no declared edge and two unrelated internal edges\n * (`api -> workflow`, `workflow -> db`) came back `attributed` on tier 2 with\n * both edges as candidates, all agreeing on no `op`. So an unbacked hop between\n * two containers of one service could never earn `usecase.step-unbacked` — the\n * one grade this join exists to produce — until the moment a fleet drew a\n * service's flow over its own containers (`core/usecases/service/flows.ts`),\n * and a landscape that draws a service as containers had the same wrong answer\n * for the same hop all along.\n *\n * Through the module's cached `resolverFor`, not a fresh `serviceResolver`: this\n * runs once per step of every use case in the fleet, and building the id map per\n * step is the cost that comment above `RESOLVERS` exists to prevent.\n */\nfunction resolvedTier(pair: CallPair, scope: StepScope): Rel[] {\n const resolve = resolverFor(scope.elements, scope.known);\n const from = resolve(pair.from);\n const to = resolve(pair.to);\n if (from === to) return [];\n return scope.relationships.filter((r) => resolve(r.source) === from && resolve(r.target) === to);\n}\n\n/**\n * Which relationship in the model backs one hop of a use case, and what it says\n * the operation is.\n *\n * Two tiers, and the second runs ONLY when the first is empty. That ordering is\n * load-bearing rather than an optimisation: the service tier necessarily\n * re-finds every exact match — an endpoint that matches literally resolves to\n * the same service trivially — so running it unconditionally would let a\n * container-level edge with a different `op` contest a step the model already\n * answers exactly, turning an `attributed` verdict into a `contested` one on a\n * document nobody changed. The step's title is read inside a tier, never across\n * them: it narrows the tier that answered and cannot reach the other.\n *\n * Relationship `kind` is deliberately not part of the match, and that is\n * measured too: at this pin `a -[http]-> b` without a `relationship http` in the\n * `specification` block is \"Could not resolve reference to RelationshipKind\n * named 'http'\" — a parse error, which under loam's standing rule means no model\n * at all — and `test/helpers/harness.ts`'s own `LANDSCAPE` declares no\n * relationship kinds. So narrowing by kind would mean a fix message asking an\n * author to write a line their document does not parse.\n */\nexport function attributeStep(step: StepReading, scope: StepScope): StepAttribution {\n const pair = callPair(step);\n const exact = scope.relationships.filter((r) => r.source === pair.from && r.target === pair.to);\n const declared = verdictOn(pair, 1, exact, step.title);\n if (declared !== undefined) return declared;\n return verdictOn(pair, 2, resolvedTier(pair, scope), step.title) ?? { ...pair, verdict: \"unbacked\" };\n}\n"]}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Which drawn elements are SERVICES — asked of a feature's delta, the way the
3
+ * fleet census asks it of the map.
4
+ *
5
+ * The census (`commands/validate/fleet/census.ts`, `drawnSystems`) is the one
6
+ * answer `validate --all` gives: an element is graded as a service when no
7
+ * ancestor already stands for one, unless it is a person, somebody else's
8
+ * system (`#external`), infrastructure the fleet runs, or a GROUPING — an
9
+ * element that contains a service rather than being one. The feature-scope
10
+ * graders asked a narrower question of their own. `service.no-requirement-delta`
11
+ * asked only "is it a `softwareSystem`", so a delta's `#external` producer was
12
+ * called a new service that owes a requirement delta; the checklist's
13
+ * `service.exists` asked "is its id dotted", so the same producer became a unit
14
+ * of work in `loam tasks`, a top-level grouping became a service called after
15
+ * its title, and the new service FEAT-101 draws inside the map's own group
16
+ * became a container nobody had to build (the real-fleet review, 2026-10-21).
17
+ * After the archive, the fleet called every one of those differently.
18
+ *
19
+ * It lives in core because those graders do (`core/coherence/`,
20
+ * `core/verify/`), and core may not import the census. `standsForService`
21
+ * below is the census's own predicate rung for rung, and the census still
22
+ * spells its copy: the two must keep answering alike until it imports this
23
+ * one.
24
+ *
25
+ * THREE DELIBERATE NARROWINGS against the census, all because a delta is not a
26
+ * whole map. An element is looked at only through ancestors the delta DECLARES
27
+ * and that are themselves groupings — the map's domain or boundary drawn around
28
+ * the services it re-declares. A level the delta does not declare may be a
29
+ * living service's own element, and one that stands for no service yet groups
30
+ * none — a new system's box, an `#external` vendor — is what its children
31
+ * belong to: a container of a service with no directory yet is that service's,
32
+ * and an endpoint inside a vendor is the vendor's (`core/gate/partners.ts`
33
+ * reads it so). And a grouping must stand for no service itself: an element
34
+ * bound to a service IS one, whatever it contains — the census's answer too
35
+ * wherever it matters, since a bound or directory-titled element never reaches
36
+ * its undocumented pass.
37
+ *
38
+ * WHAT THE DELTA'S TAGS CANNOT SAY. A delta re-declares the living elements it
39
+ * attaches to as stubs, usually untagged and always without their living tags,
40
+ * and the merge keeps the living element's tags — so "is this #external" is a
41
+ * question about the map the merge leaves, not about the stub. For an element
42
+ * no directory answers for, `readDeltaSystem` finds the living element it
43
+ * stands for and asks it the same question.
44
+ */
45
+ import { existsSync } from "node:fs";
46
+ import {} from "../likec4.js";
47
+ import { properAncestorIds } from "../../kernel/ids/fqn/ancestors.js";
48
+ import { ACTOR_KINDS, EXTERNAL_TAG, INFRA_KINDS } from "../../vocabulary/maturity.js";
49
+ import { drawnServiceElements, elementService } from "./service.js";
50
+ /**
51
+ * Does this element stand for a `services/<id>/` directory — an explicit
52
+ * `metadata { service }` binding, or a title naming one `known` holds?
53
+ */
54
+ function standsForService(e, known) {
55
+ return e.service !== undefined || known.has(e.title);
56
+ }
57
+ /**
58
+ * A GROUPING: an element that stands for no service and contains one that
59
+ * does — a domain, a boundary, an enterprise. There is nothing to bind on it,
60
+ * no requirement for it to carry and no repository that builds it; the
61
+ * services under it answer for themselves.
62
+ */
63
+ export function groupsServices(e, elements, known) {
64
+ if (standsForService(e, known))
65
+ return false;
66
+ const prefix = `${e.id}.`;
67
+ return elements.some((c) => c.id.startsWith(prefix) && standsForService(c, known));
68
+ }
69
+ /**
70
+ * Is this element a service-level SYSTEM of ours in the document `elements`
71
+ * comes from: not a person, not `#external`, not unbound infrastructure, not a
72
+ * grouping, and drawn only inside groupings that document declares.
73
+ */
74
+ function isServiceSystem(e, elements, known) {
75
+ const kind = e.kind.toLowerCase();
76
+ if (ACTOR_KINDS.has(kind) || e.tags.includes(EXTERNAL_TAG))
77
+ return false;
78
+ if (INFRA_KINDS.has(kind) && !standsForService(e, known))
79
+ return false;
80
+ const byId = new Map(elements.map((el) => [el.id, el]));
81
+ for (const id of properAncestorIds(e.id)) {
82
+ const parent = byId.get(id);
83
+ if (parent === undefined || !groupsServices(parent, elements, known))
84
+ return false;
85
+ }
86
+ return !groupsServices(e, elements, known);
87
+ }
88
+ /**
89
+ * What a tagged delta element will be on the map the merge leaves:
90
+ *
91
+ * - `none` — not a service: the delta draws it as a person, somebody else's
92
+ * system, infrastructure, a grouping or something inside another element —
93
+ * or it is the stub of a living element the map draws as one of those;
94
+ * - `living` — the stub of a service that already has a `services/<id>/`:
95
+ * this feature changes it, it does not introduce it;
96
+ * - `drawn` — a service with no directory that the living map ALREADY draws,
97
+ * so this feature re-declares it rather than introducing it either;
98
+ * - `service` — a service this feature introduces: its directory is the
99
+ * feature's own `specs/<id>/`, or nothing has drawn it before.
100
+ *
101
+ * The living map is read only for an element no directory answers for — the
102
+ * one case where the stub's missing tags can be the whole answer — so the
103
+ * common delta, whose new services all have `specs/`, never parses it. A map
104
+ * that is absent or does not parse leaves the delta to answer alone, which is
105
+ * how every element was read before the map was asked.
106
+ */
107
+ export async function readDeltaSystem(e, scope) {
108
+ const { elements, known } = scope;
109
+ if (!isServiceSystem(e, elements, known))
110
+ return "none";
111
+ const service = elementService(e);
112
+ if (scope.living.has(service))
113
+ return "living";
114
+ if (known.has(service))
115
+ return "service";
116
+ const map = await scope.livingMap();
117
+ const counterpart = map === null ? undefined : livingCounterpart(e, map, known);
118
+ if (map === null || counterpart === undefined)
119
+ return "service";
120
+ return isServiceSystem(counterpart, map.elements, known) ? "drawn" : "none";
121
+ }
122
+ /**
123
+ * The living map as `readDeltaSystem` reads it — the caller's
124
+ * `architecture/landscape.likec4` (`landscapePath`; the path arrives as an
125
+ * argument because `core/repo/` already imports this package), parsed at most
126
+ * once per reader and only when first asked; null when there is none or it
127
+ * does not parse. One reader for every grader that asks, so two of them can
128
+ * never see different maps for one element.
129
+ */
130
+ export function livingMapOf(path, load) {
131
+ let read;
132
+ return () => (read ??= (async () => {
133
+ if (!existsSync(path))
134
+ return null;
135
+ const doc = await load(path);
136
+ return doc.errors.length === 0 ? doc : null;
137
+ })());
138
+ }
139
+ /**
140
+ * The element of the living map a delta element stands for: the same id, else
141
+ * the box the map DRAWS for the same service (`drawnServiceElements` — never a
142
+ * container of some other service that happens to share a title). Undefined
143
+ * when the map draws nothing of the kind, which for an element no directory
144
+ * answers for is what makes it new.
145
+ */
146
+ function livingCounterpart(e, living, known) {
147
+ const byId = new Map(living.elements.map((el) => [el.id, el]));
148
+ const same = byId.get(e.id);
149
+ if (same !== undefined)
150
+ return same;
151
+ const drawn = drawnServiceElements(living.elements, known).get(elementService(e));
152
+ return drawn === undefined ? undefined : byId.get(drawn);
153
+ }
154
+ //# sourceMappingURL=systems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systems.js","sourceRoot":"","sources":["../../../../src/core/c4/resolve/systems.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which drawn elements are SERVICES — asked of a feature's delta, the way the\n * fleet census asks it of the map.\n *\n * The census (`commands/validate/fleet/census.ts`, `drawnSystems`) is the one\n * answer `validate --all` gives: an element is graded as a service when no\n * ancestor already stands for one, unless it is a person, somebody else's\n * system (`#external`), infrastructure the fleet runs, or a GROUPING — an\n * element that contains a service rather than being one. The feature-scope\n * graders asked a narrower question of their own. `service.no-requirement-delta`\n * asked only \"is it a `softwareSystem`\", so a delta's `#external` producer was\n * called a new service that owes a requirement delta; the checklist's\n * `service.exists` asked \"is its id dotted\", so the same producer became a unit\n * of work in `loam tasks`, a top-level grouping became a service called after\n * its title, and the new service FEAT-101 draws inside the map's own group\n * became a container nobody had to build (the real-fleet review, 2026-10-21).\n * After the archive, the fleet called every one of those differently.\n *\n * It lives in core because those graders do (`core/coherence/`,\n * `core/verify/`), and core may not import the census. `standsForService`\n * below is the census's own predicate rung for rung, and the census still\n * spells its copy: the two must keep answering alike until it imports this\n * one.\n *\n * THREE DELIBERATE NARROWINGS against the census, all because a delta is not a\n * whole map. An element is looked at only through ancestors the delta DECLARES\n * and that are themselves groupings — the map's domain or boundary drawn around\n * the services it re-declares. A level the delta does not declare may be a\n * living service's own element, and one that stands for no service yet groups\n * none — a new system's box, an `#external` vendor — is what its children\n * belong to: a container of a service with no directory yet is that service's,\n * and an endpoint inside a vendor is the vendor's (`core/gate/partners.ts`\n * reads it so). And a grouping must stand for no service itself: an element\n * bound to a service IS one, whatever it contains — the census's answer too\n * wherever it matters, since a bound or directory-titled element never reaches\n * its undocumented pass.\n *\n * WHAT THE DELTA'S TAGS CANNOT SAY. A delta re-declares the living elements it\n * attaches to as stubs, usually untagged and always without their living tags,\n * and the merge keeps the living element's tags — so \"is this #external\" is a\n * question about the map the merge leaves, not about the stub. For an element\n * no directory answers for, `readDeltaSystem` finds the living element it\n * stands for and asks it the same question.\n */\nimport { existsSync } from \"node:fs\";\nimport { type Elem, type LoadedDoc } from \"../likec4.js\";\nimport { properAncestorIds } from \"../../kernel/ids/fqn/ancestors.js\";\nimport { ACTOR_KINDS, EXTERNAL_TAG, INFRA_KINDS } from \"../../vocabulary/maturity.js\";\nimport { drawnServiceElements, elementService } from \"./service.js\";\n\n/**\n * Does this element stand for a `services/<id>/` directory — an explicit\n * `metadata { service }` binding, or a title naming one `known` holds?\n */\nfunction standsForService(e: Elem, known: ReadonlySet<string>): boolean {\n return e.service !== undefined || known.has(e.title);\n}\n\n/**\n * A GROUPING: an element that stands for no service and contains one that\n * does — a domain, a boundary, an enterprise. There is nothing to bind on it,\n * no requirement for it to carry and no repository that builds it; the\n * services under it answer for themselves.\n */\nexport function groupsServices(e: Elem, elements: readonly Elem[], known: ReadonlySet<string>): boolean {\n if (standsForService(e, known)) return false;\n const prefix = `${e.id}.`;\n return elements.some((c) => c.id.startsWith(prefix) && standsForService(c, known));\n}\n\n/**\n * Is this element a service-level SYSTEM of ours in the document `elements`\n * comes from: not a person, not `#external`, not unbound infrastructure, not a\n * grouping, and drawn only inside groupings that document declares.\n */\nfunction isServiceSystem(e: Elem, elements: readonly Elem[], known: ReadonlySet<string>): boolean {\n const kind = e.kind.toLowerCase();\n if (ACTOR_KINDS.has(kind) || e.tags.includes(EXTERNAL_TAG)) return false;\n if (INFRA_KINDS.has(kind) && !standsForService(e, known)) return false;\n const byId = new Map(elements.map((el) => [el.id, el]));\n for (const id of properAncestorIds(e.id)) {\n const parent = byId.get(id);\n if (parent === undefined || !groupsServices(parent, elements, known)) return false;\n }\n return !groupsServices(e, elements, known);\n}\n\n/** What `readDeltaSystem` reads a tagged delta element against. */\nexport interface DeltaSystemScope {\n /** Every element the delta declares — where its ancestors and children are looked up. */\n elements: readonly Elem[];\n /** The directories the merge leaves: the enumerated fleet plus the feature's own `specs/`. */\n known: ReadonlySet<string>;\n /** The enumerated fleet alone — the services that already have a `services/<id>/`. */\n living: ReadonlySet<string>;\n /** The living map (`livingMapOf`), read only when no directory answers for an element. */\n livingMap: () => Promise<LoadedDoc | null>;\n}\n\n/**\n * What a tagged delta element will be on the map the merge leaves:\n *\n * - `none` — not a service: the delta draws it as a person, somebody else's\n * system, infrastructure, a grouping or something inside another element —\n * or it is the stub of a living element the map draws as one of those;\n * - `living` — the stub of a service that already has a `services/<id>/`:\n * this feature changes it, it does not introduce it;\n * - `drawn` — a service with no directory that the living map ALREADY draws,\n * so this feature re-declares it rather than introducing it either;\n * - `service` — a service this feature introduces: its directory is the\n * feature's own `specs/<id>/`, or nothing has drawn it before.\n *\n * The living map is read only for an element no directory answers for — the\n * one case where the stub's missing tags can be the whole answer — so the\n * common delta, whose new services all have `specs/`, never parses it. A map\n * that is absent or does not parse leaves the delta to answer alone, which is\n * how every element was read before the map was asked.\n */\nexport async function readDeltaSystem(e: Elem, scope: DeltaSystemScope): Promise<\"none\" | \"living\" | \"drawn\" | \"service\"> {\n const { elements, known } = scope;\n if (!isServiceSystem(e, elements, known)) return \"none\";\n const service = elementService(e);\n if (scope.living.has(service)) return \"living\";\n if (known.has(service)) return \"service\";\n const map = await scope.livingMap();\n const counterpart = map === null ? undefined : livingCounterpart(e, map, known);\n if (map === null || counterpart === undefined) return \"service\";\n return isServiceSystem(counterpart, map.elements, known) ? \"drawn\" : \"none\";\n}\n\n/**\n * The living map as `readDeltaSystem` reads it — the caller's\n * `architecture/landscape.likec4` (`landscapePath`; the path arrives as an\n * argument because `core/repo/` already imports this package), parsed at most\n * once per reader and only when first asked; null when there is none or it\n * does not parse. One reader for every grader that asks, so two of them can\n * never see different maps for one element.\n */\nexport function livingMapOf(path: string, load: (path: string) => Promise<LoadedDoc>): () => Promise<LoadedDoc | null> {\n let read: Promise<LoadedDoc | null> | undefined;\n return () =>\n (read ??= (async () => {\n if (!existsSync(path)) return null;\n const doc = await load(path);\n return doc.errors.length === 0 ? doc : null;\n })());\n}\n\n/**\n * The element of the living map a delta element stands for: the same id, else\n * the box the map DRAWS for the same service (`drawnServiceElements` — never a\n * container of some other service that happens to share a title). Undefined\n * when the map draws nothing of the kind, which for an element no directory\n * answers for is what makes it new.\n */\nfunction livingCounterpart(e: Elem, living: LoadedDoc, known: ReadonlySet<string>): Elem | undefined {\n const byId = new Map(living.elements.map((el) => [el.id, el]));\n const same = byId.get(e.id);\n if (same !== undefined) return same;\n const drawn = drawnServiceElements(living.elements, known).get(elementService(e));\n return drawn === undefined ? undefined : byId.get(drawn);\n}\n"]}