@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,150 @@
1
+ /**
2
+ * What the parsed model declares and the splice map cannot locate — named the
3
+ * way an author can act on it.
4
+ *
5
+ * The archive splices the landscape by its text scan (`../source-scan.ts`) and
6
+ * refuses rather than splice around a declaration the scan cannot see. That
7
+ * refusal used to guess its cause: "most often two declarations sharing one
8
+ * line", whatever the map held. A fleet on 0.2.0-alpha.7 read it over a
9
+ * standalone declaration on a line of its own — the scan was blind to a second
10
+ * `model { }` block — and was asked to rewrite a valid map to satisfy a message
11
+ * that described something else. So the cause is no longer guessed where it can
12
+ * be read: LikeC4's own locator says on which line the declaration is, the
13
+ * line itself says whether anything but the declaration's own lead (`Lead`)
14
+ * precedes it there, and the scan says what it read in that place instead.
15
+ *
16
+ * A second parse of the landscape, and it is paid only on the refusal path —
17
+ * the merge decides THAT something is unlocated from the parse it already has,
18
+ * and asks this module only for the words.
19
+ */
20
+ import { LikeC4 } from "likec4";
21
+ import { maskSource } from "../source-mask.js";
22
+ /**
23
+ * The first declaration of `text` that `scan` does not locate — elements in
24
+ * the parser's order, then relationships in document order; null when every
25
+ * one is located.
26
+ *
27
+ * An element is located when the scan holds its id. A relationship has no id
28
+ * the text can carry, so it is located by POSITION: LikeC4 places each edge at
29
+ * its arrow, and each scanned statement may stand for one edge — the second
30
+ * arrow inside one scanned span is the edge the scan swallowed.
31
+ */
32
+ export async function firstUnlocated(text, scan) {
33
+ const likec4 = await LikeC4.fromSource(text, { logger: false });
34
+ try {
35
+ const model = await likec4.parsedModel();
36
+ const where = placer(text, scan);
37
+ const scanned = new Set(scan.elements.map((e) => e.id));
38
+ for (const e of model.elements()) {
39
+ if (scanned.has(e.id))
40
+ continue;
41
+ return { kind: "element", name: e.id, ...where.place(likec4.languageServices.locate({ element: e.id })?.range.start, { kind: e.kind }) };
42
+ }
43
+ const edges = [...model.relationships()].map((r) => {
44
+ const start = likec4.languageServices.locate({ relation: r.id })?.range.start;
45
+ return { name: `${r.source.id} -> ${r.target.id}`, start, offset: start === undefined ? -1 : where.offset(start) };
46
+ });
47
+ const claimed = new Set();
48
+ for (const edge of edges.sort((a, b) => a.offset - b.offset)) {
49
+ if (edge.offset === -1)
50
+ continue;
51
+ const span = scan.rels.findIndex((s) => edge.offset >= s.start && edge.offset < s.end);
52
+ if (span !== -1 && !claimed.has(span)) {
53
+ claimed.add(span);
54
+ continue;
55
+ }
56
+ return { kind: "relationship", name: edge.name, ...where.place(edge.start, "edge") };
57
+ }
58
+ // Every located edge has a statement of its own: an unlocated one, if the
59
+ // count says there is one, is among those the locator could not place.
60
+ const lost = edges.find((edge) => edge.offset === -1);
61
+ return lost === undefined ? null : { kind: "relationship", name: lost.name };
62
+ }
63
+ finally {
64
+ await likec4.dispose();
65
+ }
66
+ }
67
+ /** A whole token — not the tail of a longer id — directly before the anchor, the kind written there. */
68
+ const ELEMENT_LEAD = /(?<![\w.-])([A-Za-z_][\w-]*)\s*$/;
69
+ /** An optional source reference and an optional `-[`, directly before the anchor; always matches, possibly empty. */
70
+ const EDGE_LEAD = /(?<![\w.-])(?:[A-Za-z_][\w.-]*\s*)?(?:-\[\s*)?$/;
71
+ /**
72
+ * Where the declaration anchored at `at` starts, in the masked `code` of the
73
+ * line starting at `from`: before its lead when the code there ends in one,
74
+ * else `at`. Masked, a literal or a URI is blank, so the word before one reads
75
+ * as an edge's source here exactly as `relationHead` reads it in the scan
76
+ * (`link https://… -> zulu` is `link -> zulu` to both). Such a line is a
77
+ * scanned edge of its own, so its edge is unlocated only when an earlier
78
+ * statement's span ran on over it — and then the line-break explanation is
79
+ * the one to give, which is what dropping that word leads to.
80
+ */
81
+ function leadStart(code, span, lead) {
82
+ const before = code.slice(span.from, span.at);
83
+ const m = lead === "edge" ? EDGE_LEAD.exec(before) : ELEMENT_LEAD.exec(before);
84
+ return m === null || (lead !== "edge" && m[1] !== lead.kind) ? span.at : span.from + m.index;
85
+ }
86
+ /**
87
+ * Reads a position against the text and the scan. Lines are split the way LSP
88
+ * counts them — `\r\n`, `\r` or `\n` — because that is the count LikeC4's
89
+ * positions are in. A bare carriage return used to be the one break the scan
90
+ * did not make, and so the cause the "read it as part of" words were written
91
+ * for; `statementHead` breaks there too now, and the words stay for whatever
92
+ * run-on the scan has next.
93
+ */
94
+ function placer(text, scan) {
95
+ const starts = [0];
96
+ for (const m of text.matchAll(/\r\n|\r|\n/g))
97
+ starts.push(m.index + m[0].length);
98
+ const lineOf = (offset) => starts.findLastIndex((s) => s <= offset) + 1;
99
+ const { code } = maskSource(text);
100
+ const offset = (start) => (starts[start.line] ?? text.length) + start.character;
101
+ const heads = [
102
+ ...scan.elements
103
+ .filter((e) => e.extend !== true)
104
+ .map((e) => ({ name: `'${e.id}'`, start: e.start, end: e.bodyOpen === -1 ? e.end : e.bodyOpen })),
105
+ ...scan.rels.map((r) => ({ name: `the edge '${r.source} -> ${r.target}'`, start: r.start, end: r.end })),
106
+ ];
107
+ const place = (start, lead) => {
108
+ if (start === undefined)
109
+ return {};
110
+ const at = offset(start);
111
+ const from = starts[start.line] ?? at;
112
+ // Measured from where the declaration starts, not from the anchor: its own
113
+ // kind keyword or source is no other statement (`Lead`).
114
+ const before = code.slice(from, leadStart(code, { from, at }, lead));
115
+ // Innermost first: a nested declaration's span lies inside its parent's.
116
+ const cover = heads.filter((h) => at >= h.start && at < h.end).sort((a, b) => b.start - a.start)[0];
117
+ return {
118
+ line: start.line + 1,
119
+ ...(/\S/.test(before) ? { sharesLine: true } : {}),
120
+ ...(cover === undefined ? {} : { readAs: { name: cover.name, line: lineOf(cover.start) } }),
121
+ };
122
+ };
123
+ return { place, offset };
124
+ }
125
+ /**
126
+ * The refusal's words for one unlocated declaration of `file`. A shared line
127
+ * is named only when the line shows one, and the statement the scan read
128
+ * instead only when it read one: a guess at the cause is the message this
129
+ * replaced.
130
+ */
131
+ export function unlocatedReason(file, gap) {
132
+ const remedy = "nothing was written; rewrite it on a line of its own as `name = kind 'title' {` (an edge as `a -> b 'title'`) and re-run — " +
133
+ "and report the original shape: LikeC4 parses it, so it is a form the splice map should read";
134
+ if (gap === null)
135
+ return `${file} declares more than placement can locate, and LikeC4's locator could not say where — ${remedy}`;
136
+ const what = `${file} declares ${gap.kind === "element" ? `'${gap.name}'` : `the relationship '${gap.name}'`}`;
137
+ const where = gap.line === undefined ? "" : ` at line ${gap.line}`;
138
+ const read = gap.readAs;
139
+ if (gap.sharesLine === true) {
140
+ const other = read === undefined ? "the statement before it" : read.name;
141
+ return (`${what}${where} in a form placement cannot locate: it shares that line with ${other}, and the splice map reads ` +
142
+ `one statement per line where LikeC4 accepts two — give each its own line, then re-run`);
143
+ }
144
+ if (read !== undefined) {
145
+ return (`${what}${where} in a form placement cannot locate: the splice map read it as part of ${read.name}, which starts at ` +
146
+ `line ${read.line} — it ran that statement on past a line break LikeC4 makes — ${remedy}`);
147
+ }
148
+ return `${what}${where} in a form placement cannot locate, and the splice map read no statement there — ${remedy}`;
149
+ }
150
+ //# sourceMappingURL=unlocated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlocated.js","sourceRoot":"","sources":["../../../../src/core/c4/locate/unlocated.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What the parsed model declares and the splice map cannot locate — named the\n * way an author can act on it.\n *\n * The archive splices the landscape by its text scan (`../source-scan.ts`) and\n * refuses rather than splice around a declaration the scan cannot see. That\n * refusal used to guess its cause: \"most often two declarations sharing one\n * line\", whatever the map held. A fleet on 0.2.0-alpha.7 read it over a\n * standalone declaration on a line of its own — the scan was blind to a second\n * `model { }` block — and was asked to rewrite a valid map to satisfy a message\n * that described something else. So the cause is no longer guessed where it can\n * be read: LikeC4's own locator says on which line the declaration is, the\n * line itself says whether anything but the declaration's own lead (`Lead`)\n * precedes it there, and the scan says what it read in that place instead.\n *\n * A second parse of the landscape, and it is paid only on the refusal path —\n * the merge decides THAT something is unlocated from the parse it already has,\n * and asks this module only for the words.\n */\nimport { LikeC4 } from \"likec4\";\nimport { maskSource } from \"../source-mask.js\";\nimport type { ScannedModel } from \"../source-scan.js\";\n\n/** One declaration the parser has and the scan cannot place. */\nexport interface Unlocated {\n kind: \"element\" | \"relationship\";\n /** The element's id, or the relationship as `source -> target`, as the parser resolved them. */\n name: string;\n /** 1-based line LikeC4's locator puts the declaration on; absent when it has no answer. */\n line?: number;\n /**\n * Code other than the declaration's own lead precedes it on its own line —\n * the shared line is then a FACT, not a guess.\n */\n sharesLine?: true;\n /** The scanned statement that covers where the declaration sits, and the line that statement starts on. */\n readAs?: { name: string; line: number };\n}\n\n/**\n * The first declaration of `text` that `scan` does not locate — elements in\n * the parser's order, then relationships in document order; null when every\n * one is located.\n *\n * An element is located when the scan holds its id. A relationship has no id\n * the text can carry, so it is located by POSITION: LikeC4 places each edge at\n * its arrow, and each scanned statement may stand for one edge — the second\n * arrow inside one scanned span is the edge the scan swallowed.\n */\nexport async function firstUnlocated(text: string, scan: ScannedModel): Promise<Unlocated | null> {\n const likec4 = await LikeC4.fromSource(text, { logger: false });\n try {\n const model = await likec4.parsedModel();\n const where = placer(text, scan);\n const scanned = new Set(scan.elements.map((e) => e.id));\n for (const e of model.elements()) {\n if (scanned.has(e.id)) continue;\n return { kind: \"element\", name: e.id, ...where.place(likec4.languageServices.locate({ element: e.id })?.range.start, { kind: e.kind }) };\n }\n const edges = [...model.relationships()].map((r) => {\n const start = likec4.languageServices.locate({ relation: r.id })?.range.start;\n return { name: `${r.source.id} -> ${r.target.id}`, start, offset: start === undefined ? -1 : where.offset(start) };\n });\n const claimed = new Set<number>();\n for (const edge of edges.sort((a, b) => a.offset - b.offset)) {\n if (edge.offset === -1) continue;\n const span = scan.rels.findIndex((s) => edge.offset >= s.start && edge.offset < s.end);\n if (span !== -1 && !claimed.has(span)) {\n claimed.add(span);\n continue;\n }\n return { kind: \"relationship\", name: edge.name, ...where.place(edge.start, \"edge\") };\n }\n // Every located edge has a statement of its own: an unlocated one, if the\n // count says there is one, is among those the locator could not place.\n const lost = edges.find((edge) => edge.offset === -1);\n return lost === undefined ? null : { kind: \"relationship\", name: lost.name };\n } finally {\n await likec4.dispose();\n }\n}\n\n/** An LSP position: a 0-based line and a character within it. */\ninterface Position {\n line: number;\n character: number;\n}\n\n/**\n * The part of a declaration LikeC4's locator leaves BEFORE its anchor. It\n * points an element at its NAME and a relationship at its ARROW — inside\n * `-[…]->`, at the kind — so `container api 'API'` has its kind keyword in\n * front of the anchor and `zulu -> apple` its source. Read as \"code before\n * it\", every unlocated edge and every `kind name` element shared its line with\n * itself, and the line-break explanation `unlocatedReason` holds below the\n * shared-line one was never reached. An element's lead is a token equal to\n * its parsed kind; an edge's is its source reference (`it`, a dotted path or\n * none) and the `-[` opening a bracketed kind.\n */\ntype Lead = { kind: string } | \"edge\";\n\n/** A whole token — not the tail of a longer id — directly before the anchor, the kind written there. */\nconst ELEMENT_LEAD = /(?<![\\w.-])([A-Za-z_][\\w-]*)\\s*$/;\n/** An optional source reference and an optional `-[`, directly before the anchor; always matches, possibly empty. */\nconst EDGE_LEAD = /(?<![\\w.-])(?:[A-Za-z_][\\w.-]*\\s*)?(?:-\\[\\s*)?$/;\n\n/**\n * Where the declaration anchored at `at` starts, in the masked `code` of the\n * line starting at `from`: before its lead when the code there ends in one,\n * else `at`. Masked, a literal or a URI is blank, so the word before one reads\n * as an edge's source here exactly as `relationHead` reads it in the scan\n * (`link https://… -> zulu` is `link -> zulu` to both). Such a line is a\n * scanned edge of its own, so its edge is unlocated only when an earlier\n * statement's span ran on over it — and then the line-break explanation is\n * the one to give, which is what dropping that word leads to.\n */\nfunction leadStart(code: string, span: { from: number; at: number }, lead: Lead): number {\n const before = code.slice(span.from, span.at);\n const m = lead === \"edge\" ? EDGE_LEAD.exec(before) : ELEMENT_LEAD.exec(before);\n return m === null || (lead !== \"edge\" && m[1] !== lead.kind) ? span.at : span.from + m.index;\n}\n\n/** Everything a position tells about a declaration, and the offset arithmetic that is built on. */\ninterface Placer {\n place: (start: Position | undefined, lead: Lead) => Pick<Unlocated, \"line\" | \"sharesLine\" | \"readAs\">;\n offset: (start: Position) => number;\n}\n\n/**\n * Reads a position against the text and the scan. Lines are split the way LSP\n * counts them — `\\r\\n`, `\\r` or `\\n` — because that is the count LikeC4's\n * positions are in. A bare carriage return used to be the one break the scan\n * did not make, and so the cause the \"read it as part of\" words were written\n * for; `statementHead` breaks there too now, and the words stay for whatever\n * run-on the scan has next.\n */\nfunction placer(text: string, scan: ScannedModel): Placer {\n const starts = [0];\n for (const m of text.matchAll(/\\r\\n|\\r|\\n/g)) starts.push(m.index + m[0].length);\n const lineOf = (offset: number): number => starts.findLastIndex((s) => s <= offset) + 1;\n const { code } = maskSource(text);\n const offset = (start: Position): number => (starts[start.line] ?? text.length) + start.character;\n const heads = [\n ...scan.elements\n .filter((e) => e.extend !== true)\n .map((e) => ({ name: `'${e.id}'`, start: e.start, end: e.bodyOpen === -1 ? e.end : e.bodyOpen })),\n ...scan.rels.map((r) => ({ name: `the edge '${r.source} -> ${r.target}'`, start: r.start, end: r.end })),\n ];\n const place = (start: Position | undefined, lead: Lead): Pick<Unlocated, \"line\" | \"sharesLine\" | \"readAs\"> => {\n if (start === undefined) return {};\n const at = offset(start);\n const from = starts[start.line] ?? at;\n // Measured from where the declaration starts, not from the anchor: its own\n // kind keyword or source is no other statement (`Lead`).\n const before = code.slice(from, leadStart(code, { from, at }, lead));\n // Innermost first: a nested declaration's span lies inside its parent's.\n const cover = heads.filter((h) => at >= h.start && at < h.end).sort((a, b) => b.start - a.start)[0];\n return {\n line: start.line + 1,\n ...(/\\S/.test(before) ? { sharesLine: true as const } : {}),\n ...(cover === undefined ? {} : { readAs: { name: cover.name, line: lineOf(cover.start) } }),\n };\n };\n return { place, offset };\n}\n\n/**\n * The refusal's words for one unlocated declaration of `file`. A shared line\n * is named only when the line shows one, and the statement the scan read\n * instead only when it read one: a guess at the cause is the message this\n * replaced.\n */\nexport function unlocatedReason(file: string, gap: Unlocated | null): string {\n const remedy =\n \"nothing was written; rewrite it on a line of its own as `name = kind 'title' {` (an edge as `a -> b 'title'`) and re-run — \" +\n \"and report the original shape: LikeC4 parses it, so it is a form the splice map should read\";\n if (gap === null) return `${file} declares more than placement can locate, and LikeC4's locator could not say where — ${remedy}`;\n const what = `${file} declares ${gap.kind === \"element\" ? `'${gap.name}'` : `the relationship '${gap.name}'`}`;\n const where = gap.line === undefined ? \"\" : ` at line ${gap.line}`;\n const read = gap.readAs;\n if (gap.sharesLine === true) {\n const other = read === undefined ? \"the statement before it\" : read.name;\n return (\n `${what}${where} in a form placement cannot locate: it shares that line with ${other}, and the splice map reads ` +\n `one statement per line where LikeC4 accepts two — give each its own line, then re-run`\n );\n }\n if (read !== undefined) {\n return (\n `${what}${where} in a form placement cannot locate: the splice map read it as part of ${read.name}, which starts at ` +\n `line ${read.line} — it ran that statement on past a line break LikeC4 makes — ${remedy}`\n );\n }\n return `${what}${where} in a form placement cannot locate, and the splice map read no statement there — ${remedy}`;\n}\n"]}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * The DEPLOYMENT model a document declares — which physical nodes exist, which
3
+ * containers are instanced where, and what talks to what across them.
4
+ *
5
+ * A `deployment { }` block has been legal in a docs repo since LikeC4 could
6
+ * parse one, and until this reader existed loam saw none of it. The parser
7
+ * resolved every `instanceOf` — so a container renamed out from under a
8
+ * deployment node already failed the gate — and after that no requirement could
9
+ * name a node, no `#obl-` tag on a datacenter was graded, and the context pack
10
+ * an agent implements from did not mention topology at all. Integrity held at
11
+ * the level of syntax and was absent at the level of requirements. ROADMAP's
12
+ * deployment axis and `meta/docs/features/FEAT-1-the-deployment-axis/` are the
13
+ * item and the specification; this module is its first half.
14
+ *
15
+ * ## Why this is a `parsed/` reader and not a package of its own
16
+ *
17
+ * It is the same job the three modules beside it do: take one corner of
18
+ * LikeC4's parse output and hand back a loam-neutral record, defensively, with
19
+ * its shape assumptions owned outright. `readSpecification`, `readDynamicViews`
20
+ * and `readViewIds` are all called on the line above `flattenModel(model)` in
21
+ * each of loam's three loaders, and so is this. FEAT-1's delta said
22
+ * `src/core/c4/deploy/`; a package holding this alone would be a directory
23
+ * pretending to be a subject (docs/DESIGN.md), and the delta was corrected
24
+ * rather than the code bent to it — which is what an unarchived feature is for.
25
+ *
26
+ * **`parsed/` is still AT its five-file limit.** The global style-id census
27
+ * rode into `view-ids.ts` because it IS that module's subject — one more
28
+ * table asked the same question — and the next reader that is not a census
29
+ * still needs the split first. The seam to split on is the one this file does
30
+ * not share: `dynamic-views.ts` and `view-ids.ts` read `$data`, this one and
31
+ * `specification.ts` read the model surface.
32
+ *
33
+ * ## What is read, and what is deliberately left
34
+ *
35
+ * Nodes, instances and relationships, with the ids, titles and tags each
36
+ * carries. MEASURED at the 1.59.2 pin, and every line of it needed measuring
37
+ * rather than assuming:
38
+ *
39
+ * - `nodes()` returns deployment NODES only — an `instanceOf` is not among
40
+ * them, which is why instances are a second list rather than a filter.
41
+ * - an instance carries the LOGICAL element it deploys as `element.id`, and
42
+ * that is the whole join back to the C4 model: `eu.a.dbA -> orders.db`.
43
+ * - both nodes and instances carry `tags`, so `#obl-geo` on a datacenter is
44
+ * readable — the fail-open case that made this an item.
45
+ * - a relationship carries `tags` and `metadata` DIRECTLY (`r.metadata`, no
46
+ * `$relationship` indirection), and both model stages return byte-identical
47
+ * results, which is what lets `test/likec4-model-parity.test.ts` cover this
48
+ * read with the substitution it already pins.
49
+ * - a document with NO `deployment { }` block still returns a deployment
50
+ * object whose three iterators are empty. Absent and empty are therefore the
51
+ * same answer here, deliberately: no reader may distinguish "declares none"
52
+ * from "could not be read", because the second is what the defensive returns
53
+ * below produce and a fleet that draws no topology owes loam nothing.
54
+ *
55
+ * `metadata` is NOT carried on the records below, and neither is an instance's
56
+ * `kind`. Both are readable; no check reads them yet, and a field carried
57
+ * before a caller needs it is a field nobody notices going wrong. Add one with
58
+ * the check that consumes it.
59
+ */
60
+ /** No topology — what an absent block and an unreadable one both come back as. */
61
+ export const NO_DEPLOYMENT = { nodes: [], instances: [], relationships: [] };
62
+ /** Is there anything here? The opt-in every deployment check is gated on. */
63
+ export function hasDeployment(d) {
64
+ return d.nodes.length > 0 || d.instances.length > 0 || d.relationships.length > 0;
65
+ }
66
+ /** A record, or nothing — the parse surface hands back `unknown`. */
67
+ function record(v) {
68
+ return v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
69
+ }
70
+ /** An iterable produced by `name()`, or an empty one when the surface is not what we measured. */
71
+ function iterate(source, name) {
72
+ const fn = source[name];
73
+ if (typeof fn !== "function")
74
+ return [];
75
+ try {
76
+ const out = fn.call(source);
77
+ // A generator satisfies this; so does an array. Anything else is a shape
78
+ // change upstream, and the honest answer to one is "no topology read".
79
+ return out !== null && typeof out === "object" && Symbol.iterator in out
80
+ ? [...out]
81
+ : [];
82
+ }
83
+ catch {
84
+ return [];
85
+ }
86
+ }
87
+ /** A string field, or the empty string — never `undefined` leaking into a record. */
88
+ function str(o, key) {
89
+ const v = o[key];
90
+ return typeof v === "string" ? v : "";
91
+ }
92
+ /** Tags as loam models them: a plain array of strings, always present. */
93
+ function tags(o) {
94
+ const v = o["tags"];
95
+ return Array.isArray(v) ? v.filter((t) => typeof t === "string") : [];
96
+ }
97
+ /**
98
+ * Read the deployment model off an awaited `parsedModel()`.
99
+ *
100
+ * Defensive throughout, in the shape `readViewIds` established: an entry
101
+ * missing the one field that identifies it is DROPPED rather than reported with
102
+ * a hole, and a surface that is not what was measured yields an empty model
103
+ * instead of throwing. A check that cannot see the topology must say
104
+ * could-not-look through its own silence, never fail the fleet's parse.
105
+ */
106
+ export function readDeployment(model) {
107
+ const root = record(model);
108
+ if (root === undefined)
109
+ return NO_DEPLOYMENT;
110
+ const deployment = record(root["deployment"]);
111
+ if (deployment === undefined)
112
+ return NO_DEPLOYMENT;
113
+ const nodes = [];
114
+ for (const raw of iterate(deployment, "nodes")) {
115
+ const n = record(raw);
116
+ if (n === undefined)
117
+ continue;
118
+ const id = str(n, "id");
119
+ if (id === "")
120
+ continue;
121
+ nodes.push({ id, kind: str(n, "kind"), title: str(n, "title"), tags: tags(n) });
122
+ }
123
+ const instances = [];
124
+ for (const raw of iterate(deployment, "instances")) {
125
+ const i = record(raw);
126
+ if (i === undefined)
127
+ continue;
128
+ const id = str(i, "id");
129
+ // The logical element is the whole point of the record. An instance whose
130
+ // element cannot be read joins to nothing, so reporting it would put a
131
+ // deployed thing on the map that no requirement could ever cover.
132
+ const element = record(i["element"]);
133
+ if (id === "" || element === undefined)
134
+ continue;
135
+ const elementId = str(element, "id");
136
+ if (elementId === "")
137
+ continue;
138
+ instances.push({ id, element: elementId, title: str(i, "title"), tags: tags(i) });
139
+ }
140
+ const relationships = [];
141
+ for (const raw of iterate(deployment, "relationships")) {
142
+ const r = record(raw);
143
+ if (r === undefined)
144
+ continue;
145
+ const source = record(r["source"]);
146
+ const target = record(r["target"]);
147
+ if (source === undefined || target === undefined)
148
+ continue;
149
+ const from = str(source, "id");
150
+ const to = str(target, "id");
151
+ if (from === "" || to === "")
152
+ continue;
153
+ // LikeC4 reports an untitled edge as `title: null` — normalized to the
154
+ // declared optional exactly as `flattenModel` does for the logical model.
155
+ const title = typeof r["title"] === "string" ? r["title"] : undefined;
156
+ relationships.push({ source: from, target: to, ...(title === undefined ? {} : { title }), tags: tags(r) });
157
+ }
158
+ return { nodes, instances, relationships };
159
+ }
160
+ //# sourceMappingURL=deployment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/deployment.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The DEPLOYMENT model a document declares — which physical nodes exist, which\n * containers are instanced where, and what talks to what across them.\n *\n * A `deployment { }` block has been legal in a docs repo since LikeC4 could\n * parse one, and until this reader existed loam saw none of it. The parser\n * resolved every `instanceOf` — so a container renamed out from under a\n * deployment node already failed the gate — and after that no requirement could\n * name a node, no `#obl-` tag on a datacenter was graded, and the context pack\n * an agent implements from did not mention topology at all. Integrity held at\n * the level of syntax and was absent at the level of requirements. ROADMAP's\n * deployment axis and `meta/docs/features/FEAT-1-the-deployment-axis/` are the\n * item and the specification; this module is its first half.\n *\n * ## Why this is a `parsed/` reader and not a package of its own\n *\n * It is the same job the three modules beside it do: take one corner of\n * LikeC4's parse output and hand back a loam-neutral record, defensively, with\n * its shape assumptions owned outright. `readSpecification`, `readDynamicViews`\n * and `readViewIds` are all called on the line above `flattenModel(model)` in\n * each of loam's three loaders, and so is this. FEAT-1's delta said\n * `src/core/c4/deploy/`; a package holding this alone would be a directory\n * pretending to be a subject (docs/DESIGN.md), and the delta was corrected\n * rather than the code bent to it — which is what an unarchived feature is for.\n *\n * **`parsed/` is still AT its five-file limit.** The global style-id census\n * rode into `view-ids.ts` because it IS that module's subject — one more\n * table asked the same question — and the next reader that is not a census\n * still needs the split first. The seam to split on is the one this file does\n * not share: `dynamic-views.ts` and `view-ids.ts` read `$data`, this one and\n * `specification.ts` read the model surface.\n *\n * ## What is read, and what is deliberately left\n *\n * Nodes, instances and relationships, with the ids, titles and tags each\n * carries. MEASURED at the 1.59.2 pin, and every line of it needed measuring\n * rather than assuming:\n *\n * - `nodes()` returns deployment NODES only — an `instanceOf` is not among\n * them, which is why instances are a second list rather than a filter.\n * - an instance carries the LOGICAL element it deploys as `element.id`, and\n * that is the whole join back to the C4 model: `eu.a.dbA -> orders.db`.\n * - both nodes and instances carry `tags`, so `#obl-geo` on a datacenter is\n * readable — the fail-open case that made this an item.\n * - a relationship carries `tags` and `metadata` DIRECTLY (`r.metadata`, no\n * `$relationship` indirection), and both model stages return byte-identical\n * results, which is what lets `test/likec4-model-parity.test.ts` cover this\n * read with the substitution it already pins.\n * - a document with NO `deployment { }` block still returns a deployment\n * object whose three iterators are empty. Absent and empty are therefore the\n * same answer here, deliberately: no reader may distinguish \"declares none\"\n * from \"could not be read\", because the second is what the defensive returns\n * below produce and a fleet that draws no topology owes loam nothing.\n *\n * `metadata` is NOT carried on the records below, and neither is an instance's\n * `kind`. Both are readable; no check reads them yet, and a field carried\n * before a caller needs it is a field nobody notices going wrong. Add one with\n * the check that consumes it.\n */\n\n/** One deployment node — a region, a datacenter, a cluster; the kinds are the fleet's own. */\nexport interface DeployNode {\n /** The dotted path LikeC4 gives it: `eu.dcA.k8sA`. */\n id: string;\n /** The `deploymentNode` kind the specification declares. */\n kind: string;\n title: string;\n tags: string[];\n}\n\n/** One deployed instance of a logical element. */\nexport interface DeployInstance {\n id: string;\n /**\n * The id of the C4 element this instance deploys — the join back to the\n * logical model, and the only reason an instance is worth reading at all.\n */\n element: string;\n title: string;\n tags: string[];\n}\n\n/** One edge between deployment objects — replication, failover, a network hop. */\nexport interface DeployRel {\n source: string;\n target: string;\n title?: string;\n tags: string[];\n}\n\n/** A document's whole deployment model, flattened. */\nexport interface DeploymentModel {\n nodes: DeployNode[];\n instances: DeployInstance[];\n relationships: DeployRel[];\n}\n\n/** No topology — what an absent block and an unreadable one both come back as. */\nexport const NO_DEPLOYMENT: DeploymentModel = { nodes: [], instances: [], relationships: [] };\n\n/** Is there anything here? The opt-in every deployment check is gated on. */\nexport function hasDeployment(d: DeploymentModel): boolean {\n return d.nodes.length > 0 || d.instances.length > 0 || d.relationships.length > 0;\n}\n\n/** A record, or nothing — the parse surface hands back `unknown`. */\nfunction record(v: unknown): Record<string, unknown> | undefined {\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;\n}\n\n/** An iterable produced by `name()`, or an empty one when the surface is not what we measured. */\nfunction iterate(source: Record<string, unknown>, name: string): unknown[] {\n const fn = source[name];\n if (typeof fn !== \"function\") return [];\n try {\n const out: unknown = (fn as () => unknown).call(source);\n // A generator satisfies this; so does an array. Anything else is a shape\n // change upstream, and the honest answer to one is \"no topology read\".\n return out !== null && typeof out === \"object\" && Symbol.iterator in (out as object)\n ? [...(out as Iterable<unknown>)]\n : [];\n } catch {\n return [];\n }\n}\n\n/** A string field, or the empty string — never `undefined` leaking into a record. */\nfunction str(o: Record<string, unknown>, key: string): string {\n const v = o[key];\n return typeof v === \"string\" ? v : \"\";\n}\n\n/** Tags as loam models them: a plain array of strings, always present. */\nfunction tags(o: Record<string, unknown>): string[] {\n const v = o[\"tags\"];\n return Array.isArray(v) ? v.filter((t): t is string => typeof t === \"string\") : [];\n}\n\n/**\n * Read the deployment model off an awaited `parsedModel()`.\n *\n * Defensive throughout, in the shape `readViewIds` established: an entry\n * missing the one field that identifies it is DROPPED rather than reported with\n * a hole, and a surface that is not what was measured yields an empty model\n * instead of throwing. A check that cannot see the topology must say\n * could-not-look through its own silence, never fail the fleet's parse.\n */\nexport function readDeployment(model: unknown): DeploymentModel {\n const root = record(model);\n if (root === undefined) return NO_DEPLOYMENT;\n const deployment = record(root[\"deployment\"]);\n if (deployment === undefined) return NO_DEPLOYMENT;\n\n const nodes: DeployNode[] = [];\n for (const raw of iterate(deployment, \"nodes\")) {\n const n = record(raw);\n if (n === undefined) continue;\n const id = str(n, \"id\");\n if (id === \"\") continue;\n nodes.push({ id, kind: str(n, \"kind\"), title: str(n, \"title\"), tags: tags(n) });\n }\n\n const instances: DeployInstance[] = [];\n for (const raw of iterate(deployment, \"instances\")) {\n const i = record(raw);\n if (i === undefined) continue;\n const id = str(i, \"id\");\n // The logical element is the whole point of the record. An instance whose\n // element cannot be read joins to nothing, so reporting it would put a\n // deployed thing on the map that no requirement could ever cover.\n const element = record(i[\"element\"]);\n if (id === \"\" || element === undefined) continue;\n const elementId = str(element, \"id\");\n if (elementId === \"\") continue;\n instances.push({ id, element: elementId, title: str(i, \"title\"), tags: tags(i) });\n }\n\n const relationships: DeployRel[] = [];\n for (const raw of iterate(deployment, \"relationships\")) {\n const r = record(raw);\n if (r === undefined) continue;\n const source = record(r[\"source\"]);\n const target = record(r[\"target\"]);\n if (source === undefined || target === undefined) continue;\n const from = str(source, \"id\");\n const to = str(target, \"id\");\n if (from === \"\" || to === \"\") continue;\n // LikeC4 reports an untitled edge as `title: null` — normalized to the\n // declared optional exactly as `flattenModel` does for the logical model.\n const title = typeof r[\"title\"] === \"string\" ? r[\"title\"] : undefined;\n relationships.push({ source: from, target: to, ...(title === undefined ? {} : { title }), tags: tags(r) });\n }\n\n return { nodes, instances, relationships };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-views.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/dynamic-views.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `dynamic view`s a document DECLARES, read off LikeC4's parsed model.\n *\n * This is the only module in loam permitted to touch `$data`, and\n * `scripts/arch-check.mjs` enforces that rather than trusting it (docs/DESIGN.md\n * rule 26). The confinement is the point: `$data` is public and typed but thinly\n * documented for this use, so an upstream shape change has a blast radius of one\n * file, and `test/likec4-view-shape.test.ts` is the tripwire that fires before\n * the change reaches a check.\n *\n * It takes the WHOLE model as `unknown` rather than a `$data` slice handed in by\n * its caller, and that is not squeamishness — naming `$data` in `../likec4.ts`\n * would violate the containment scan this module exists behind. Callers pass the\n * model; every assumption about LikeC4's record shape lives here.\n *\n * Everything is defensive, in the same posture as `./specification.ts`: a shape\n * this reader cannot make sense of degrades to ZERO views, never to a wrong one\n * and never to a throw inside a validate run. That asymmetry is deliberate and\n * is loam's standing rule for a suspended axis — a check with no views to grade\n * reports could-not-look, while a check fed a half-read view would report\n * nothing-wrong about a flow it never saw.\n */\nimport { descText } from \"./values.js\";\n\n/** One hop of a declared use case: an ordered edge between two elements. */\nexport interface ParsedStep {\n /**\n * 1-based pre-order position among the view's LEAF steps — what lets a finding\n * say \"step 4 of Checkout\". Group wrappers do not consume an ordinal: a\n * `loop` is a bracket around hops, not a hop, and counting it would shift\n * every number after it away from what the author sees on the diagram.\n */\n ordinal: number;\n /**\n * The step's endpoints, ALREADY ORIENTED — see `isBackward`. Dotted FQN\n * strings naming elements, never relationship ids: a step says who talks to\n * whom, and which relationship backs that is a join loam performs later.\n */\n source: string;\n target: string;\n /** The step's label, absent when the author wrote none. */\n title?: string;\n /** A `notes '...'` block on the step, decoded from LikeC4's rich-text shape. */\n notes?: string;\n /**\n * True when the author wrote the hop as a reply — `a <- b` rather than\n * `b -> a`. LOAD-BEARING, and measured: LikeC4 records `a <- b 'reply'` as\n * `{source: b, target: a, isBackward: true}` — already reversed AND flagged —\n * while a forward step carries no such key at all. `source`/`target` above are\n * therefore correct as they stand and need no flipping; the flag is carried so\n * a check can tell a return hop from a call, and so a message naming the\n * failing step can suggest the `<-` spelling to an author who wrote the arrow\n * the long way round.\n */\n isBackward: boolean;\n /**\n * LikeC4's own path to the step inside the view (`/steps@1/steps@0`), carried\n * verbatim. It is the only stable handle on a nested step — two hops between\n * the same pair inside different `loop` blocks are otherwise indistinguishable\n * — and it is message text, never identity: nothing joins on it.\n *\n * OPTIONAL, and that is a deliberate refusal to make it load-bearing. Every\n * leaf carries one at the 1.59.2 pin, so requiring it would look free — and\n * would mean that an upstream release which stopped emitting it turned every\n * use case in the fleet into a view with zero steps. That is the silent wrong\n * answer this module exists to avoid: a hop loam cannot cite is still a hop it\n * must grade.\n */\n astPath?: string;\n}\n\n/** One declared `dynamic view`, flattened to what rule 26 permits reading. */\nexport interface ParsedView {\n id: string;\n /**\n * The name LikeC4 knows the document by, and it is only a PATH under\n * `loadProject`: there it is relative to the project root, so\n * `usecases/checkout.likec4` inside `architecture/`. Under the per-file\n * loaders it is whatever that loader called the document — `source.c4` for\n * `loadSource`, the basename for `loadBatch` — because there is no project\n * for it to be relative to.\n *\n * So: name a file with it only when the views came from `loadProject`. That\n * is the only reader that has one, which is also the only reader that needs\n * one — a use case lives in a file of its own now, and a finding that cannot\n * name that file sends its reader to the landscape to look for something\n * that is not there.\n *\n * Absent for anything LikeC4 synthesized rather than read off a file, which\n * is how the census reader tells the two apart.\n */\n sourcePath?: string;\n /**\n * The view's declared tags, `[]` when it declares none. Normalized here\n * because LikeC4 reads an untagged view's tags as `null`, and a caller\n * testing `.length` on that would throw inside a validate run.\n *\n * Tags live on the VIEW and nowhere else: a step cannot carry tags, and\n * `metadata` on a step is a hard parse error. That is measured, and it is why\n * a use case is opted in as a whole rather than hop by hop.\n */\n tags: string[];\n title?: string;\n description?: string;\n /** The declared hops, in author order, groups flattened away. */\n steps: ParsedStep[];\n}\n\n/** A record, or nothing — `v` is `unknown` off an untyped parse. */\nfunction record(v: unknown): Record<string, unknown> | undefined {\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;\n}\n\n/** The value at `key`, when it is a non-empty string. */\nfunction str(rec: Record<string, unknown>, key: string): string | undefined {\n const v = rec[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\n/**\n * Every leaf step under `entries`, depth-first in author order.\n *\n * Steps NEST: a `loop 'until accepted' { ... }` arrives as an entry with\n * `_type: \"loop\"`, a title, its own `steps[]`, and NO endpoints and NO `astPath`\n * of its own (measured). A walk that treated every entry as a hop would read the\n * bracket as a step and then fail to find a relationship backing it — inventing\n * a finding out of a grouping the author drew for legibility.\n *\n * The test is structural rather than a list of group keywords (`loop`, `par`,\n * ...): an entry with a nested `steps` array is a group whatever LikeC4 calls\n * it, and an entry with two string endpoints is a hop. A future group kind\n * therefore flattens correctly on the day it ships instead of on the day\n * somebody notices.\n */\nfunction leaves(entries: unknown, out: Record<string, unknown>[]): void {\n if (!Array.isArray(entries)) return;\n for (const entry of entries) {\n const rec = record(entry);\n if (!rec) continue;\n if (Array.isArray(rec[\"steps\"])) {\n leaves(rec[\"steps\"], out);\n continue;\n }\n if (typeof rec[\"source\"] === \"string\" && typeof rec[\"target\"] === \"string\") out.push(rec);\n }\n}\n\n/** One leaf entry as a `ParsedStep`. Every leaf yields one; nothing is dropped. */\nfunction step(rec: Record<string, unknown>, ordinal: number): ParsedStep {\n const astPath = str(rec, \"astPath\");\n const title = str(rec, \"title\");\n const notes = descText(rec[\"notes\"]);\n return {\n ordinal,\n source: rec[\"source\"] as string,\n target: rec[\"target\"] as string,\n ...(title === undefined ? {} : { title }),\n ...(notes === undefined ? {} : { notes }),\n // Present-and-true is the only truthy form LikeC4 emits; absent means\n // forward. Read as a boolean rather than carried as `true | undefined` so a\n // caller cannot accidentally test `=== false` against a key that is never\n // written.\n isBackward: rec[\"isBackward\"] === true,\n ...(astPath === undefined ? {} : { astPath }),\n };\n}\n\n/**\n * The dynamic views one parsed document declares, in the record's own order.\n *\n * `model` is the awaited `parsedModel()` result. The `await` matters and is the\n * mistake every draft of this made: `$data` is `undefined` on the unresolved\n * promise, so a reader handed the promise reports a document with views as\n * having none — a silent zero, which is the one wrong answer this module is\n * written to avoid.\n *\n * Two entries are dropped on the way out, for different reasons:\n *\n * - Anything whose `_type` is not `\"dynamic\"`. LikeC4 synthesises an `index`\n * view into EVERY document, including one with no `views` block at all\n * (measured), so reporting element views would be reporting a fiction the\n * author never wrote. A census that must see authored element views wants the\n * other filter rule 26 states — `sourcePath !== undefined` — and must not\n * reuse this function to get it.\n * - Anything with no `id`. There is nothing to name in a finding, and a view\n * loam cannot name is a view it cannot ask anybody to fix.\n */\nexport function readDynamicViews(model: unknown): ParsedView[] {\n const data = record(record(model)?.[\"$data\"]);\n const views = record(data?.[\"views\"]);\n if (!views) return [];\n\n const out: ParsedView[] = [];\n for (const raw of Object.values(views)) {\n const rec = record(raw);\n if (!rec || rec[\"_type\"] !== \"dynamic\") continue;\n const id = str(rec, \"id\");\n if (id === undefined) continue;\n\n const found: Record<string, unknown>[] = [];\n leaves(rec[\"steps\"], found);\n const steps = found.map((leaf, i) => step(leaf, i + 1));\n\n const title = str(rec, \"title\");\n const description = descText(rec[\"description\"]);\n const sourcePath = str(rec, \"sourcePath\");\n out.push({\n id,\n ...(sourcePath === undefined ? {} : { sourcePath }),\n tags: Array.isArray(rec[\"tags\"]) ? rec[\"tags\"].filter((t): t is string => typeof t === \"string\") : [],\n ...(title === undefined ? {} : { title }),\n ...(description === undefined ? {} : { description }),\n steps,\n });\n }\n return out;\n}\n"]}
1
+ {"version":3,"file":"dynamic-views.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/dynamic-views.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA4FvC,oEAAoE;AACpE,SAAS,MAAM,CAAC,CAAU;IACxB,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,SAAS,CAAC;AACtG,CAAC;AAED,yDAAyD;AACzD,SAAS,GAAG,CAAC,GAA4B,EAAE,GAAW;IACpD,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,MAAM,CAAC,OAAgB,EAAE,GAA8B;IAC9D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO;IACpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,SAAS,IAAI,CAAC,GAA4B,EAAE,OAAe;IACzD,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,OAAO;QACL,OAAO;QACP,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAW;QAC/B,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAW;QAC/B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,sEAAsE;QACtE,4EAA4E;QAC5E,0EAA0E;QAC1E,WAAW;QACX,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC,KAAK,IAAI;QACtC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS;YAAE,SAAS;QACjD,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,EAAE,KAAK,SAAS;YAAE,SAAS;QAE/B,MAAM,KAAK,GAA8B,EAAE,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAExD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC1C,GAAG,CAAC,IAAI,CAAC;YACP,EAAE;YACF,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;YACnD,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;YACrG,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;YACrD,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * The `dynamic view`s a document DECLARES, read off LikeC4's parsed model.\n *\n * This is the only module in loam permitted to touch `$data`, and\n * `scripts/arch-check.mjs` enforces that rather than trusting it (docs/DESIGN.md\n * rule 26). The confinement is the point: `$data` is public and typed but thinly\n * documented for this use, so an upstream shape change has a blast radius of one\n * file, and `test/likec4-view-shape.test.ts` is the tripwire that fires before\n * the change reaches a check.\n *\n * It takes the WHOLE model as `unknown` rather than a `$data` slice handed in by\n * its caller, and that is not squeamishness — naming `$data` in `../likec4.ts`\n * would violate the containment scan this module exists behind. Callers pass the\n * model; every assumption about LikeC4's record shape lives here.\n *\n * Everything is defensive, in the same posture as `./specification.ts`: a shape\n * this reader cannot make sense of degrades to ZERO views, never to a wrong one\n * and never to a throw inside a validate run. That asymmetry is deliberate and\n * is loam's standing rule for a suspended axis — a check with no views to grade\n * reports could-not-look, while a check fed a half-read view would report\n * nothing-wrong about a flow it never saw.\n */\nimport { descText } from \"./values.js\";\n\n/** One hop of a declared use case: an ordered edge between two elements. */\nexport interface ParsedStep {\n /**\n * 1-based pre-order position among the view's LEAF steps — what lets a finding\n * say \"step 4 of Checkout\". Group wrappers do not consume an ordinal: a\n * `loop` is a bracket around hops, not a hop, and counting it would shift\n * every number after it away from what the author sees on the diagram.\n */\n ordinal: number;\n /**\n * The step's endpoints, ALREADY ORIENTED — see `isBackward`. Dotted FQN\n * strings naming elements, never relationship ids: a step says who talks to\n * whom, and which relationship backs that is a join loam performs later.\n */\n source: string;\n target: string;\n /**\n * The step's label, absent when the author wrote none. Carried verbatim,\n * because one join reads its opening characters: a title that opens with a\n * candidate relationship's operation id or message name, set off by\n * punctuation, picks that relationship where the map joins the pair by\n * several (`pickedByTitle` in `../resolve/steps.ts`).\n */\n title?: string;\n /** A `notes '...'` block on the step, decoded from LikeC4's rich-text shape. */\n notes?: string;\n /**\n * True when the author wrote the hop as a reply — `a <- b` rather than\n * `b -> a`. LOAD-BEARING, and measured: LikeC4 records `a <- b 'reply'` as\n * `{source: b, target: a, isBackward: true}` — already reversed AND flagged —\n * while a forward step carries no such key at all. `source`/`target` above are\n * therefore correct as they stand and need no flipping; the flag is carried so\n * a check can tell a return hop from a call, and so a message naming the\n * failing step can suggest the `<-` spelling to an author who wrote the arrow\n * the long way round.\n */\n isBackward: boolean;\n /**\n * LikeC4's own path to the step inside the view (`/steps@1/steps@0`), carried\n * verbatim. It is the only stable handle on a nested step — two hops between\n * the same pair inside different `loop` blocks are otherwise indistinguishable\n * — and it is message text, never identity: nothing joins on it.\n *\n * OPTIONAL, and that is a deliberate refusal to make it load-bearing. Every\n * leaf carries one at the 1.59.2 pin, so requiring it would look free — and\n * would mean that an upstream release which stopped emitting it turned every\n * use case in the fleet into a view with zero steps. That is the silent wrong\n * answer this module exists to avoid: a hop loam cannot cite is still a hop it\n * must grade.\n */\n astPath?: string;\n}\n\n/** One declared `dynamic view`, flattened to what rule 26 permits reading. */\nexport interface ParsedView {\n id: string;\n /**\n * The name LikeC4 knows the document by, and it is only a PATH under\n * `loadProject`: there it is relative to the project root, so\n * `usecases/checkout.likec4` inside `architecture/`. Under the per-file\n * loaders it is whatever that loader called the document — `source.c4` for\n * `loadSource`, the basename for `loadBatch` — because there is no project\n * for it to be relative to.\n *\n * So: name a file with it only when the views came from `loadProject`. That\n * is the only reader that has one, which is also the only reader that needs\n * one — a use case lives in a file of its own now, and a finding that cannot\n * name that file sends its reader to the landscape to look for something\n * that is not there.\n *\n * Absent for anything LikeC4 synthesized rather than read off a file, which\n * is how the census reader tells the two apart.\n */\n sourcePath?: string;\n /**\n * The view's declared tags, `[]` when it declares none. Normalized here\n * because LikeC4 reads an untagged view's tags as `null`, and a caller\n * testing `.length` on that would throw inside a validate run.\n *\n * Tags live on the VIEW and nowhere else: a step cannot carry tags, and\n * `metadata` on a step is a hard parse error. That is measured, and it is why\n * a use case is opted in as a whole rather than hop by hop.\n */\n tags: string[];\n title?: string;\n description?: string;\n /** The declared hops, in author order, groups flattened away. */\n steps: ParsedStep[];\n}\n\n/** A record, or nothing — `v` is `unknown` off an untyped parse. */\nfunction record(v: unknown): Record<string, unknown> | undefined {\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;\n}\n\n/** The value at `key`, when it is a non-empty string. */\nfunction str(rec: Record<string, unknown>, key: string): string | undefined {\n const v = rec[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\n/**\n * Every leaf step under `entries`, depth-first in author order.\n *\n * Steps NEST: a `loop 'until accepted' { ... }` arrives as an entry with\n * `_type: \"loop\"`, a title, its own `steps[]`, and NO endpoints and NO `astPath`\n * of its own (measured). A walk that treated every entry as a hop would read the\n * bracket as a step and then fail to find a relationship backing it — inventing\n * a finding out of a grouping the author drew for legibility.\n *\n * The test is structural rather than a list of group keywords (`loop`, `par`,\n * ...): an entry with a nested `steps` array is a group whatever LikeC4 calls\n * it, and an entry with two string endpoints is a hop. A future group kind\n * therefore flattens correctly on the day it ships instead of on the day\n * somebody notices.\n */\nfunction leaves(entries: unknown, out: Record<string, unknown>[]): void {\n if (!Array.isArray(entries)) return;\n for (const entry of entries) {\n const rec = record(entry);\n if (!rec) continue;\n if (Array.isArray(rec[\"steps\"])) {\n leaves(rec[\"steps\"], out);\n continue;\n }\n if (typeof rec[\"source\"] === \"string\" && typeof rec[\"target\"] === \"string\") out.push(rec);\n }\n}\n\n/** One leaf entry as a `ParsedStep`. Every leaf yields one; nothing is dropped. */\nfunction step(rec: Record<string, unknown>, ordinal: number): ParsedStep {\n const astPath = str(rec, \"astPath\");\n const title = str(rec, \"title\");\n const notes = descText(rec[\"notes\"]);\n return {\n ordinal,\n source: rec[\"source\"] as string,\n target: rec[\"target\"] as string,\n ...(title === undefined ? {} : { title }),\n ...(notes === undefined ? {} : { notes }),\n // Present-and-true is the only truthy form LikeC4 emits; absent means\n // forward. Read as a boolean rather than carried as `true | undefined` so a\n // caller cannot accidentally test `=== false` against a key that is never\n // written.\n isBackward: rec[\"isBackward\"] === true,\n ...(astPath === undefined ? {} : { astPath }),\n };\n}\n\n/**\n * The dynamic views one parsed document declares, in the record's own order.\n *\n * `model` is the awaited `parsedModel()` result. The `await` matters and is the\n * mistake every draft of this made: `$data` is `undefined` on the unresolved\n * promise, so a reader handed the promise reports a document with views as\n * having none — a silent zero, which is the one wrong answer this module is\n * written to avoid.\n *\n * Two entries are dropped on the way out, for different reasons:\n *\n * - Anything whose `_type` is not `\"dynamic\"`. LikeC4 synthesises an `index`\n * view into EVERY document, including one with no `views` block at all\n * (measured), so reporting element views would be reporting a fiction the\n * author never wrote. A census that must see authored element views wants the\n * other filter rule 26 states — `sourcePath !== undefined` — and must not\n * reuse this function to get it.\n * - Anything with no `id`. There is nothing to name in a finding, and a view\n * loam cannot name is a view it cannot ask anybody to fix.\n */\nexport function readDynamicViews(model: unknown): ParsedView[] {\n const data = record(record(model)?.[\"$data\"]);\n const views = record(data?.[\"views\"]);\n if (!views) return [];\n\n const out: ParsedView[] = [];\n for (const raw of Object.values(views)) {\n const rec = record(raw);\n if (!rec || rec[\"_type\"] !== \"dynamic\") continue;\n const id = str(rec, \"id\");\n if (id === undefined) continue;\n\n const found: Record<string, unknown>[] = [];\n leaves(rec[\"steps\"], found);\n const steps = found.map((leaf, i) => step(leaf, i + 1));\n\n const title = str(rec, \"title\");\n const description = descText(rec[\"description\"]);\n const sourcePath = str(rec, \"sourcePath\");\n out.push({\n id,\n ...(sourcePath === undefined ? {} : { sourcePath }),\n tags: Array.isArray(rec[\"tags\"]) ? rec[\"tags\"].filter((t): t is string => typeof t === \"string\") : [],\n ...(title === undefined ? {} : { title }),\n ...(description === undefined ? {} : { description }),\n steps,\n });\n }\n return out;\n}\n"]}
@@ -9,7 +9,9 @@
9
9
  * (`../likec4.ts`) that calls them, because the adapter is at its line limit and
10
10
  * because "decode a value LikeC4 left untyped" is a subject of its own: the
11
11
  * specification reader beside this file is the same subject on a different
12
- * corner of the same object.
12
+ * corner of the same object. The ORDER relationships come back in is one more
13
+ * such corner — LikeC4 promises none, and the one it happens to give changes
14
+ * between runs — so `compareRels` lives here too.
13
15
  */
14
16
  /**
15
17
  * Read one string key out of a LikeC4 `metadata { ... }` block. Four keys carry
@@ -54,4 +56,55 @@ export function descText(d) {
54
56
  }
55
57
  return undefined;
56
58
  }
59
+ /**
60
+ * The ONE order every loader hands relationships back in: by source, then
61
+ * target, title, `op`, `publishes`, `consumes` and tags — plain code-unit
62
+ * compares, so the order is a function of the documents' text alone and never
63
+ * of the locale, the machine or the run. Two edges equal on every key are equal
64
+ * in every field `Rel` (`../likec4.ts`) carries, so which of them comes first
65
+ * cannot be seen. `flattenModel` there sorts with it; it lives here, beside the
66
+ * other reads of LikeC4's parse output that cannot be taken at face value,
67
+ * because the adapter is at its line limit.
68
+ *
69
+ * LikeC4 does not give one, for two measured reasons (1.59.2). Its parsed
70
+ * model indexes relations in a plain object keyed by an id HASHED FROM THE
71
+ * DOCUMENT'S URI — and every loader stages documents under a fresh
72
+ * crypto-random folder per invocation (`../workspace.ts` says why), so the ids
73
+ * change on every run. A JavaScript object enumerates integer-like keys
74
+ * numerically ahead of every other key, so whenever a hash comes out all
75
+ * digits (about one hash in 2,200) that edge jumps to the front of the list,
76
+ * on that run only. And inside a project of several documents the base order
77
+ * is whichever file Langium's concurrent folder walk finished reading first.
78
+ * The first reason is what failed a scheduled CI run on 2026-09-28:
79
+ * `loadProjectBatch` and `loadProject` returned one service's two edges in
80
+ * opposite orders on an unchanged commit — and on a fleet map of a few hundred
81
+ * edges it would reorder some run's findings far more often than that.
82
+ *
83
+ * Declaration order is therefore NOT what a reader gets, and nothing may read
84
+ * meaning into the position of an edge: what the `Rel` list promises is the
85
+ * same order for the same documents, which is what the text report and the
86
+ * `--json` envelope being diffed between runs and machines needs.
87
+ */
88
+ export function compareRels(a, b) {
89
+ // An absent field sorts ahead of every present one, the empty string
90
+ // included, so "no title" and "an empty title" cannot tie. Tags are LikeC4
91
+ // identifiers, which cannot contain the comma that joins them.
92
+ const opt = (value) => (value === undefined ? "" : `=${value}`);
93
+ const keys = (r) => [
94
+ r.source,
95
+ r.target,
96
+ opt(r.title),
97
+ opt(r.op),
98
+ opt(r.publishes),
99
+ opt(r.consumes),
100
+ r.tags.join(","),
101
+ ];
102
+ const ka = keys(a);
103
+ const kb = keys(b);
104
+ for (let i = 0; i < ka.length; i++) {
105
+ if (ka[i] !== kb[i])
106
+ return ka[i] < kb[i] ? -1 : 1;
107
+ }
108
+ return 0;
109
+ }
57
110
  //# sourceMappingURL=values.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"values.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/values.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,OAAO,CAAC,CAAU,EAAE,GAAW;IAC7C,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAI,CAA6B,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;QACpC,4EAA4E;QAC5E,2EAA2E;QAC3E,8EAA8E;QAC9E,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,kDAAkD;QAClD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACjB,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["/**\n * The corners of LikeC4's parse output that are typed `unknown`, decoded.\n *\n * LikeC4 models `metadata { ... }` values and a `description` as open shapes —\n * a value may be a string, an array, or a rich-text object, and the declared\n * type says none of that. Every one of those crossings needs a defensive read\n * that answers \"not present\" rather than throwing, and each read encodes a\n * defect somebody hit. They live together here, one module below the adapter\n * (`../likec4.ts`) that calls them, because the adapter is at its line limit and\n * because \"decode a value LikeC4 left untyped\" is a subject of its own: the\n * specification reader beside this file is the same subject on a different\n * corner of the same object.\n */\n\n/**\n * Read one string key out of a LikeC4 `metadata { ... }` block. Four keys carry\n * loam's spines: `op` on a relationship (the OpenAPI operationId it calls),\n * `publishes`/`consumes` on a relationship (the AsyncAPI message it produces or\n * receives), and `service` on an element (the services/<id> directory it stands\n * for). Every one of them is read by BOTH model readers — the parsed one\n * (`flattenModel` in `../likec4.ts`, which calls this) and the text scanner\n * `scanModel` uses for archive's splice map — because a key only one of them\n * sees is a key the merge silently drops. Elements with no metadata come back as\n * `{}`, so a missing key is indistinguishable from a missing block — both mean\n * \"not bound\".\n */\nexport function metaKey(m: unknown, key: string): string | undefined {\n if (m && typeof m === \"object\") {\n const v = (m as Record<string, unknown>)[key];\n if (typeof v === \"string\") return v;\n // A key written TWICE in one block comes back as an array, accepted with no\n // error, and reading only the string form dropped every value — so an edge\n // naming two operations reported as naming none, `c4.op-link-missing` telling\n // the author the opposite of what they wrote. First wins, matching the text\n // scanner's `keyedLiteral`: the two readers disagreeing about a binding is\n // exactly what the paragraph above exists to prevent. Later values stay\n // dropped — one id per key per edge is the model.\n if (Array.isArray(v) && typeof v[0] === \"string\") return v[0];\n }\n return undefined;\n}\n\n/** LikeC4 descriptions can be a string or a rich-text object ({ txt } / { text } / { md }). */\nexport function descText(d: unknown): string | undefined {\n if (typeof d === \"string\") return d;\n if (d && typeof d === \"object\") {\n const o = d as Record<string, unknown>;\n for (const key of [\"txt\", \"text\", \"md\", \"value\"]) {\n const v = o[key];\n if (typeof v === \"string\") return v;\n }\n }\n return undefined;\n}\n"]}
1
+ {"version":3,"file":"values.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/values.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,OAAO,CAAC,CAAU,EAAE,GAAW;IAC7C,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAI,CAA6B,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;QACpC,4EAA4E;QAC5E,2EAA2E;QAC3E,8EAA8E;QAC9E,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,kDAAkD;QAClD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACjB,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,CAAU;IAChD,qEAAqE;IACrE,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,GAAG,GAAG,CAAC,KAAyB,EAAU,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAC5F,MAAM,IAAI,GAAG,CAAC,CAAU,EAAY,EAAE,CAAC;QACrC,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM;QACR,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QACZ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACT,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QAChB,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;QACf,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;KACjB,CAAC;IACF,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC,CAAC,CAAE,GAAG,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC","sourcesContent":["/**\n * The corners of LikeC4's parse output that are typed `unknown`, decoded.\n *\n * LikeC4 models `metadata { ... }` values and a `description` as open shapes —\n * a value may be a string, an array, or a rich-text object, and the declared\n * type says none of that. Every one of those crossings needs a defensive read\n * that answers \"not present\" rather than throwing, and each read encodes a\n * defect somebody hit. They live together here, one module below the adapter\n * (`../likec4.ts`) that calls them, because the adapter is at its line limit and\n * because \"decode a value LikeC4 left untyped\" is a subject of its own: the\n * specification reader beside this file is the same subject on a different\n * corner of the same object. The ORDER relationships come back in is one more\n * such corner — LikeC4 promises none, and the one it happens to give changes\n * between runs — so `compareRels` lives here too.\n */\n\n/**\n * Read one string key out of a LikeC4 `metadata { ... }` block. Four keys carry\n * loam's spines: `op` on a relationship (the OpenAPI operationId it calls),\n * `publishes`/`consumes` on a relationship (the AsyncAPI message it produces or\n * receives), and `service` on an element (the services/<id> directory it stands\n * for). Every one of them is read by BOTH model readers — the parsed one\n * (`flattenModel` in `../likec4.ts`, which calls this) and the text scanner\n * `scanModel` uses for archive's splice map — because a key only one of them\n * sees is a key the merge silently drops. Elements with no metadata come back as\n * `{}`, so a missing key is indistinguishable from a missing block — both mean\n * \"not bound\".\n */\nexport function metaKey(m: unknown, key: string): string | undefined {\n if (m && typeof m === \"object\") {\n const v = (m as Record<string, unknown>)[key];\n if (typeof v === \"string\") return v;\n // A key written TWICE in one block comes back as an array, accepted with no\n // error, and reading only the string form dropped every value — so an edge\n // naming two operations reported as naming none, `c4.op-link-missing` telling\n // the author the opposite of what they wrote. First wins, matching the text\n // scanner's `keyedLiteral`: the two readers disagreeing about a binding is\n // exactly what the paragraph above exists to prevent. Later values stay\n // dropped — one id per key per edge is the model.\n if (Array.isArray(v) && typeof v[0] === \"string\") return v[0];\n }\n return undefined;\n}\n\n/** LikeC4 descriptions can be a string or a rich-text object ({ txt } / { text } / { md }). */\nexport function descText(d: unknown): string | undefined {\n if (typeof d === \"string\") return d;\n if (d && typeof d === \"object\") {\n const o = d as Record<string, unknown>;\n for (const key of [\"txt\", \"text\", \"md\", \"value\"]) {\n const v = o[key];\n if (typeof v === \"string\") return v;\n }\n }\n return undefined;\n}\n\n/**\n * The fields of a relationship its order is decided on — `Rel` in\n * `../likec4.ts`, stated structurally because that module imports this one and\n * a type imported back would close a cycle.\n */\nexport interface RelKeys {\n readonly source: string;\n readonly target: string;\n readonly title?: string;\n readonly op?: string;\n readonly publishes?: string;\n readonly consumes?: string;\n readonly tags: readonly string[];\n}\n\n/**\n * The ONE order every loader hands relationships back in: by source, then\n * target, title, `op`, `publishes`, `consumes` and tags — plain code-unit\n * compares, so the order is a function of the documents' text alone and never\n * of the locale, the machine or the run. Two edges equal on every key are equal\n * in every field `Rel` (`../likec4.ts`) carries, so which of them comes first\n * cannot be seen. `flattenModel` there sorts with it; it lives here, beside the\n * other reads of LikeC4's parse output that cannot be taken at face value,\n * because the adapter is at its line limit.\n *\n * LikeC4 does not give one, for two measured reasons (1.59.2). Its parsed\n * model indexes relations in a plain object keyed by an id HASHED FROM THE\n * DOCUMENT'S URI — and every loader stages documents under a fresh\n * crypto-random folder per invocation (`../workspace.ts` says why), so the ids\n * change on every run. A JavaScript object enumerates integer-like keys\n * numerically ahead of every other key, so whenever a hash comes out all\n * digits (about one hash in 2,200) that edge jumps to the front of the list,\n * on that run only. And inside a project of several documents the base order\n * is whichever file Langium's concurrent folder walk finished reading first.\n * The first reason is what failed a scheduled CI run on 2026-09-28:\n * `loadProjectBatch` and `loadProject` returned one service's two edges in\n * opposite orders on an unchanged commit — and on a fleet map of a few hundred\n * edges it would reorder some run's findings far more often than that.\n *\n * Declaration order is therefore NOT what a reader gets, and nothing may read\n * meaning into the position of an edge: what the `Rel` list promises is the\n * same order for the same documents, which is what the text report and the\n * `--json` envelope being diffed between runs and machines needs.\n */\nexport function compareRels(a: RelKeys, b: RelKeys): number {\n // An absent field sorts ahead of every present one, the empty string\n // included, so \"no title\" and \"an empty title\" cannot tie. Tags are LikeC4\n // identifiers, which cannot contain the comma that joins them.\n const opt = (value: string | undefined): string => (value === undefined ? \"\" : `=${value}`);\n const keys = (r: RelKeys): string[] => [\n r.source,\n r.target,\n opt(r.title),\n opt(r.op),\n opt(r.publishes),\n opt(r.consumes),\n r.tags.join(\",\"),\n ];\n const ka = keys(a);\n const kb = keys(b);\n for (let i = 0; i < ka.length; i++) {\n if (ka[i] !== kb[i]) return ka[i]! < kb[i]! ? -1 : 1;\n }\n return 0;\n}\n"]}
@@ -16,8 +16,17 @@
16
16
  * safer when each one owns its shape assumptions outright — the same reason
17
17
  * `./specification.ts` states.
18
18
  *
19
+ * Two censuses over one record now: view ids off `$data.views`, and since
20
+ * 2026-09-03 the global style ids off `$data.globals.styles` — the same
21
+ * question (which authored ids exist, for a collision or a reference) asked of
22
+ * a second corner, so the generated subsystem views can reference a style
23
+ * group only when the project actually declares it. The second rode into this
24
+ * module rather than a file of its own because `parsed/` sits AT its five-file
25
+ * cap and the census IS this module's subject; `./deployment.ts`'s banner
26
+ * records the seam the next non-census reader still needs.
27
+ *
19
28
  * Ids only, each with the file that claims it. What the view SHOWS stays
20
- * unread, in any document, forever.
29
+ * unread, in any document, forever — and so does what a style group SAYS.
21
30
  */
22
31
  /**
23
32
  * The shape LikeC4 mints for a view its author never named — `view of svc { }`
@@ -72,4 +81,33 @@ export function readViewIds(model) {
72
81
  }
73
82
  return out;
74
83
  }
84
+ /**
85
+ * Every global style id the document declares, sorted.
86
+ *
87
+ * `$data.globals.styles` is keyed by id and holds one entry per `styleGroup`
88
+ * OR single-rule `style` declared under `global { }` — both forms land in the
89
+ * one table, measured at the 1.59.2 pin and pinned in
90
+ * test/likec4-view-shape.test.ts. Keys only: what a group SAYS — its targets
91
+ * and colours — is a rendering instruction, and docs/DESIGN.md rule 26 keeps
92
+ * loam out of those on purpose; the one consumer (the generated subsystem
93
+ * views) asks whether an id is declared, nothing more. Sorted so a document
94
+ * declaring two groups yields one order under every loader — the parity rule
95
+ * `readViewIds` states for the minted-id case, applied here to `Object.keys`
96
+ * order, which is the record's and not the author's.
97
+ *
98
+ * Defensive like everything here: a shape this cannot read is ZERO ids, so the
99
+ * generated views then reference nothing — the same file they rendered before
100
+ * the read existed, never a reference to a name loam guessed. That direction
101
+ * is the safe one: a missed declaration costs a palette, while an invented
102
+ * reference is a parse error that blanks the whole `architecture/` project in
103
+ * the renderer.
104
+ */
105
+ export function readGlobalStyleIds(model) {
106
+ const styles = record(record(record(record(model)?.["$data"])?.["globals"])?.["styles"]);
107
+ if (!styles)
108
+ return [];
109
+ return Object.keys(styles)
110
+ .filter((id) => id.length > 0)
111
+ .sort();
112
+ }
75
113
  //# sourceMappingURL=view-ids.js.map