@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,352 @@
1
+ /**
2
+ * The ONE literal fact loam reads out of the root `likec4.config.json`: which
3
+ * paths that file excludes from the renderer's root project, and whether a
4
+ * given directory or document is one of them.
5
+ *
6
+ * Until this module existed loam read NOTHING out of that file. It wrote it
7
+ * once (`core/docs.ts`, `loam init --create`), never re-read it, and the team
8
+ * owned it from then on — the ownership rule `core/repo/tree/render/projects.ts`
9
+ * still states for the per-service files. That rule is kept everywhere it still
10
+ * applies; this is the one exception, and it is earned by what report #05
11
+ * measured: a model that EXTENDS the fleet map lives in the ROOT project, so
12
+ * the root's `exclude` decides whether the renderer can see it at all. A repo
13
+ * whose root config still says `services/**` — every repo scaffolded before
14
+ * this axis — renders such a model as a box with nothing inside it, and no
15
+ * check could say why, because nothing here had ever opened the file.
16
+ *
17
+ * READ here, and written back by one command only (`loam subsystem sync`,
18
+ * through `standaloneExclude` in `../service-model/renderer.ts`). Nothing else
19
+ * about the file is interpreted: `exclude` is a glob list in a tool's own
20
+ * config, and the entries loam does not recognise as naming a directory are
21
+ * left exactly where the team put them.
22
+ *
23
+ * IT LIVES BELOW `../project/` BECAUSE THE PROJECT LOADER IS A READER. The
24
+ * architecture document set is the one list every reader shares, and it has to
25
+ * drop a `.likec4` the root project excludes — a palette under an excluded path
26
+ * otherwise yields a `global style` reference the fleet project cannot resolve,
27
+ * `likec4 validate` Invalid on every run and `loam validate --all` at 0 errors
28
+ * (verification 2026-09-04, W5). `../service-model/` already imports
29
+ * `../project/`, so this could not stay there without reversing that edge.
30
+ */
31
+ import { readFile } from "node:fs/promises";
32
+ import { isRecord } from "../../kernel/records.js";
33
+ import { rootProjectPath } from "../../repo/paths.js";
34
+ /** The `services/` prefix every tree path is spelled under, in the config and on disk alike. */
35
+ const SERVICES = "services";
36
+ /**
37
+ * The root project's `exclude` list, or null when the file cannot be read as a
38
+ * project config at all.
39
+ *
40
+ * The three answers are deliberately distinct, because two of them mean
41
+ * opposite things to a grader:
42
+ *
43
+ * - `[]` — the file is a JSON object that excludes nothing (or names no
44
+ * `exclude` key at all, which is the same thing to the renderer). Every
45
+ * extending model is visible; a standalone one is in the root project where
46
+ * it cannot parse.
47
+ * - a list — the entries as written, in file order.
48
+ * - `null` — absent, unreadable, not JSON, or JSON whose `exclude` is not a
49
+ * list of strings. loam does not know what the renderer will do, so it says
50
+ * nothing rather than guessing: the findings that read this list are silent
51
+ * on null, `subsystem sync` leaves such a file alone, and the project loader
52
+ * filters nothing. Failing closed here means the team keeps a file loam
53
+ * cannot parse; failing open would mean loam asserting the renderer excludes
54
+ * something on evidence it does not have.
55
+ */
56
+ export async function readRootExclude(docsDir) {
57
+ const parsed = await readRootProject(docsDir);
58
+ if (parsed === null)
59
+ return null;
60
+ const exclude = parsed["exclude"];
61
+ if (exclude === undefined)
62
+ return [];
63
+ if (!Array.isArray(exclude))
64
+ return null;
65
+ // A predicate rather than a cast, so the narrowing below is the compiler's
66
+ // conclusion from a check that actually ran: an `exclude` holding a number is
67
+ // a file loam does not understand, and understanding it half way is how a
68
+ // grader ends up asserting a glob nobody wrote.
69
+ const entries = exclude;
70
+ return entries.every((entry) => typeof entry === "string") ? [...entries] : null;
71
+ }
72
+ /**
73
+ * The root project file as a JSON object, or null when it is absent or is not
74
+ * one — the ONE reader, shared with the one writer that re-parses it before a
75
+ * rewrite (`commands/subsystem/txn/exclude.ts`).
76
+ *
77
+ * A LEADING BYTE-ORDER MARK IS STRIPPED, and that is not tidiness: PowerShell's
78
+ * `Out-File` writes `ef bb bf` by default on this platform, so a Windows shell
79
+ * saving the very file loam wrote left `JSON.parse` throwing while the renderer
80
+ * — whose reader tolerates the mark — went on applying every entry in the list.
81
+ * loam then graded nothing at all: `validate --all` silent about an exclusion in
82
+ * full force, `doctor` healthy, `subsystem sync` reporting the list unreadable
83
+ * and naming a fault (`not a JSON object with a string exclude list`) that was
84
+ * not the one present (re-verification 2026-09-04, area C item 3). One reader
85
+ * means the grade and the rewrite can never disagree about whether the file
86
+ * parses.
87
+ */
88
+ async function readRootProject(docsDir) {
89
+ let text;
90
+ try {
91
+ text = await readFile(rootProjectPath(docsDir), "utf8");
92
+ }
93
+ catch {
94
+ // Absent reads as "loam cannot say", not as "excludes nothing": a docs repo
95
+ // with no root project file has no root project either, and the renderer
96
+ // opened there shows nothing at all — a different problem, with a different
97
+ // finding (`doctor.likec4-config-missing`).
98
+ return null;
99
+ }
100
+ return parseRootProject(text);
101
+ }
102
+ /** The byte-order mark PowerShell's `Out-File` prepends by default on this platform. */
103
+ const BOM = "";
104
+ /** The same parse, over bytes a caller already read. Exported for the rewrite path only. */
105
+ export function parseRootProject(text) {
106
+ let parsed;
107
+ try {
108
+ parsed = JSON.parse(text.startsWith(BOM) ? text.slice(BOM.length) : text);
109
+ }
110
+ catch {
111
+ // Not JSON is the team's file to fix, and nothing here may rewrite it.
112
+ return null;
113
+ }
114
+ return isRecord(parsed) ? parsed : null;
115
+ }
116
+ /** A PATH as segments — `/` or `\`, either spelling, no empty parts, no `.`. Never an ENTRY: see `rendererEntry`. */
117
+ function segments(path) {
118
+ return path.split(/[\\/]/).filter((s) => s.length > 0 && s !== ".");
119
+ }
120
+ function rendererEntry(entry) {
121
+ if (entry.includes("\\"))
122
+ return null;
123
+ let rest = entry;
124
+ let anchored = false;
125
+ if (rest.startsWith("./")) {
126
+ rest = rest.slice(2);
127
+ anchored = true;
128
+ // `.//x` — the dot survives as a literal directory name, so the pattern
129
+ // names a path nothing on disk has.
130
+ if (rest.startsWith("/"))
131
+ return null;
132
+ }
133
+ else if (rest.startsWith("../")) {
134
+ return null;
135
+ }
136
+ const trailing = rest.endsWith("/");
137
+ const parts = [];
138
+ for (const part of rest.split("/")) {
139
+ if (part.length === 0 || part === ".")
140
+ continue;
141
+ if (part === "..") {
142
+ if (parts.pop() === undefined)
143
+ return null;
144
+ continue;
145
+ }
146
+ parts.push(part);
147
+ }
148
+ // An entry that normalises to nothing at all is the project folder itself,
149
+ // which `contains: true` makes a match for every document under it.
150
+ const glob = parts.length === 0 ? "**" : `${parts.join("/")}${trailing ? "/" : ""}`;
151
+ return { glob, anchored: anchored && parts.length > 0 };
152
+ }
153
+ /** The expression one entry matches with, or null when it matches nothing — `rendererEntry` says which. */
154
+ function matcherFor(parsed, glob) {
155
+ return new RegExp(`^${globSource(parsed.anchored ? glob : prefixed(glob))}`);
156
+ }
157
+ /**
158
+ * An entry's segments with the trailing bare wildcards taken off — the question
159
+ * "which directory is this line ABOUT", asked of the four spellings that name
160
+ * one: `services/pay` doubled-star, that entry with a trailing `/*` after it,
161
+ * `services/pay/*` and `services/pay/` all reduce to `services/pay`. A wildcard
162
+ * that is NOT trailing survives — one segment of its own — because a starred
163
+ * middle segment picks one directory level and no more.
164
+ *
165
+ * It is NOT the covering rule. What an entry HIDES is `entryRegex` below, and
166
+ * the two answer differently on purpose: `services/pay/**` is about the
167
+ * directory `services/pay`, while the bare `services/pay` hides
168
+ * `services/payment-service` as well (measured — see below).
169
+ *
170
+ * It takes the NORMALISED glob, never the raw entry: an entry is split on `/`
171
+ * alone, because a backslash is a literal to the renderer and splitting on it
172
+ * here was what made `services\order-service\**` read as naming a directory it
173
+ * hides nothing in.
174
+ */
175
+ function patternSegments(glob) {
176
+ const parts = glob.split("/").filter((part) => part.length > 0);
177
+ while (parts.length > 0 && (parts.at(-1) === "*" || parts.at(-1) === "**"))
178
+ parts.pop();
179
+ return parts;
180
+ }
181
+ /**
182
+ * One `exclude` entry as the expression the RENDERER matches paths with:
183
+ * anchored at the start of a path segment, and NOT anchored at the end.
184
+ *
185
+ * MEASURED, at the 1.59.2 pin, on a scratch fleet of five documents —
186
+ * `architecture/landscape.likec4`, `architecture/usecases/uc.likec4`,
187
+ * `services/payment-service/model.likec4`,
188
+ * `services/payments/payment-service/model.likec4` and
189
+ * `services/order-service/model.likec4` — counting the "N files" `npx likec4
190
+ * validate .` reports with one entry in the list:
191
+ *
192
+ * services/zzz 5 services/pay 3
193
+ * services/payment-service 4 a star as the middle segment 4
194
+ * services/pay/** 5 architect 3
195
+ * architecture/*.likec4 4 *.likec4 0
196
+ * ervices 5 ces/payment-service 5
197
+ * payment-service 3 services/** 2
198
+ * services 2 services/payment-service/* hides a nested file too
199
+ *
200
+ * Four rules come out of that table, and every one of them contradicts what
201
+ * this module asserted before (verification 2026-09-04, review C): the match is
202
+ * a raw PREFIX with no end anchor (`services/pay` takes both payment trees,
203
+ * `architect` takes all of `architecture/`); it must START on a `/` boundary
204
+ * (`ervices` and `ces/payment-service` take nothing); `*` stays inside one
205
+ * segment (`architecture/*.likec4` leaves `architecture/usecases/uc.likec4`);
206
+ * and a glob form does NOT prefix-match (`services/pay/**` needs the literal
207
+ * separator, so it takes nothing from `services/payment-service`).
208
+ *
209
+ * The shape falls out of what LikeC4 does with the list: an entry that does not
210
+ * already begin with a doubled star is prefixed with one, joined onto the
211
+ * project folder and handed to picomatch with `contains: true` — and `contains`
212
+ * is exactly the missing end anchor. So the optional any-directories group
213
+ * `globSource` emits for a leading doubled star IS that prefix, and the absent
214
+ * `$` is that option.
215
+ *
216
+ * NULL when the entry hides nothing at all — `rendererEntry` names the two
217
+ * spellings that do, and every caller has to skip such an entry rather than
218
+ * treat it as covering: a warning derived from one sent a team after a line the
219
+ * renderer ignores, and `subsystem sync` then deleted it.
220
+ */
221
+ function entryRegex(entry) {
222
+ const parsed = rendererEntry(entry);
223
+ return parsed === null ? null : matcherFor(parsed, parsed.glob);
224
+ }
225
+ /** An entry as LikeC4 spells it before matching: a doubled star in front, unless it has one. */
226
+ function prefixed(entry) {
227
+ return entry.startsWith("**") ? entry : `**/${entry}`;
228
+ }
229
+ /**
230
+ * A glob as a regular-expression SOURCE. `**` crosses `/` and, followed by a
231
+ * separator, may stand for no directory at all (a doubled star between two literals matches with no directory between them, which
232
+ * is what makes a doubled-star-then-star entry cover a model directly under `services/`);
233
+ * `*` and `?` never cross one.
234
+ */
235
+ function globSource(pattern) {
236
+ let source = "";
237
+ let i = 0;
238
+ while (i < pattern.length) {
239
+ if (pattern.startsWith("**/", i)) {
240
+ source += "(?:.*/)?";
241
+ i += 3;
242
+ continue;
243
+ }
244
+ if (pattern.startsWith("**", i)) {
245
+ source += ".*";
246
+ i += 2;
247
+ continue;
248
+ }
249
+ const c = pattern[i];
250
+ if (c === "*")
251
+ source += "[^/]*";
252
+ else if (c === "?")
253
+ source += "[^/]";
254
+ else
255
+ source += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
256
+ i += 1;
257
+ }
258
+ return source;
259
+ }
260
+ /**
261
+ * The entry that hides `relPath` from the root project, or null when nothing in
262
+ * the list does. `relPath` is a docs-relative FILE path, spelled either way
263
+ * round the slash.
264
+ *
265
+ * Returned VERBATIM because every message that carries it asks its reader to
266
+ * delete or narrow THAT line, and an entry re-spelled by loam is a line they
267
+ * would go looking for and not find.
268
+ */
269
+ export function excludingPath(exclude, relPath) {
270
+ const path = segments(relPath).join("/");
271
+ if (path.length === 0)
272
+ return null;
273
+ for (const entry of exclude) {
274
+ if (entryRegex(entry)?.test(path) === true)
275
+ return entry;
276
+ }
277
+ return null;
278
+ }
279
+ /**
280
+ * The entry that hides a SERVICE TREE — the directory and everything under it —
281
+ * or null. The tree's spelling is normalised here because the repository writes
282
+ * it both ways: `serviceTreePath` includes the leading `services/` and the
283
+ * entries `standaloneExclude` writes are built from the part under it, and
284
+ * getting that wrong would silently answer "not excluded" for every service.
285
+ *
286
+ * A DIRECTORY IS NOT A FILE, and that is why this is not `excludingPath` on the
287
+ * directory's own path. `services/payment-service/**` hides every document in
288
+ * that tree while matching no prefix of the directory string itself — the
289
+ * pattern wants the separator the directory path ends before. So a second arm
290
+ * asks the other half: does the directory the entry NAMES (`patternSegments`,
291
+ * the trailing wildcards off) contain this tree? Asking it of the named
292
+ * directory rather than of the raw prefix is what keeps `services/pay/**` about
293
+ * `services/pay` and away from `services/payment-service`, which is exactly
294
+ * where the renderer leaves it.
295
+ *
296
+ * What this deliberately does NOT report is an entry that hides only PART of a
297
+ * tree (`services/x/model`, say). The four findings that read this answer are
298
+ * about a directory the renderer either loads or does not, and half of one is a
299
+ * state `subsystem sync` has no entry to write for.
300
+ */
301
+ export function excludingEntry(exclude, treePath) {
302
+ const tree = segments(treePath).join("/");
303
+ if (tree.length === 0)
304
+ return null;
305
+ const full = tree === SERVICES || tree.startsWith(`${SERVICES}/`) ? tree : `${SERVICES}/${tree}`;
306
+ for (const entry of exclude) {
307
+ if (coversTree(entry, full))
308
+ return entry;
309
+ }
310
+ return null;
311
+ }
312
+ /** Does one entry hide `dir` and everything under it? The two arms `excludingEntry` documents. */
313
+ function coversTree(entry, dir) {
314
+ const parsed = rendererEntry(entry);
315
+ // An entry that hides nothing covers nothing — the backslash spelling, which
316
+ // loam used to read as covering the tree while the renderer loaded every file
317
+ // in it (measured: 8 of 8), warning `service.model-excluded` about a healthy
318
+ // service and then deleting the team's line on the next sync.
319
+ if (parsed === null)
320
+ return false;
321
+ if (matcherFor(parsed, parsed.glob).test(dir))
322
+ return true;
323
+ const named = patternSegments(parsed.glob);
324
+ if (named.length === 0)
325
+ return false;
326
+ // The named directory, or an ancestor of it, matched on a segment boundary —
327
+ // never mid-segment, which is the whole difference between this arm and the
328
+ // prefix one above.
329
+ return new RegExp(`${matcherFor(parsed, named.join("/")).source}(?:/|$)`).test(dir);
330
+ }
331
+ /**
332
+ * The DIRECTORY an entry names, `/`-joined, or null when the entry does not
333
+ * plainly name one — a wildcard segment between two literal ones, or the
334
+ * leading doubled star of the `node_modules` glob.
335
+ *
336
+ * The trailing wildcards are what get stripped: `services/pay` doubled-star and
337
+ * the three other spellings `patternSegments` lists all name `services/pay`. It
338
+ * is the AUTHORSHIP question the writer asks — "did loam write this line, and is
339
+ * it about a directory the enumeration returns?" — never the covering one, which
340
+ * is `excludingEntry`'s.
341
+ */
342
+ export function excludedDirectory(entry) {
343
+ const parsed = rendererEntry(entry);
344
+ // An entry that hides nothing names nothing either, so a backslash spelling is
345
+ // never loam's to rewrite: `subsystem sync` leaves it exactly where the team
346
+ // put it, which is the only honest answer about a line the renderer ignores.
347
+ if (parsed === null)
348
+ return null;
349
+ const parts = patternSegments(parsed.glob);
350
+ return parts.some((part) => part.includes("*")) ? null : parts.join("/");
351
+ }
352
+ //# sourceMappingURL=exclude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exclude.js","sourceRoot":"","sources":["../../../../src/core/c4/root-project/exclude.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The ONE literal fact loam reads out of the root `likec4.config.json`: which\n * paths that file excludes from the renderer's root project, and whether a\n * given directory or document is one of them.\n *\n * Until this module existed loam read NOTHING out of that file. It wrote it\n * once (`core/docs.ts`, `loam init --create`), never re-read it, and the team\n * owned it from then on — the ownership rule `core/repo/tree/render/projects.ts`\n * still states for the per-service files. That rule is kept everywhere it still\n * applies; this is the one exception, and it is earned by what report #05\n * measured: a model that EXTENDS the fleet map lives in the ROOT project, so\n * the root's `exclude` decides whether the renderer can see it at all. A repo\n * whose root config still says `services/**` — every repo scaffolded before\n * this axis — renders such a model as a box with nothing inside it, and no\n * check could say why, because nothing here had ever opened the file.\n *\n * READ here, and written back by one command only (`loam subsystem sync`,\n * through `standaloneExclude` in `../service-model/renderer.ts`). Nothing else\n * about the file is interpreted: `exclude` is a glob list in a tool's own\n * config, and the entries loam does not recognise as naming a directory are\n * left exactly where the team put them.\n *\n * IT LIVES BELOW `../project/` BECAUSE THE PROJECT LOADER IS A READER. The\n * architecture document set is the one list every reader shares, and it has to\n * drop a `.likec4` the root project excludes — a palette under an excluded path\n * otherwise yields a `global style` reference the fleet project cannot resolve,\n * `likec4 validate` Invalid on every run and `loam validate --all` at 0 errors\n * (verification 2026-09-04, W5). `../service-model/` already imports\n * `../project/`, so this could not stay there without reversing that edge.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { isRecord } from \"../../kernel/records.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\nimport { rootProjectPath } from \"../../repo/paths.js\";\n\n/** The `services/` prefix every tree path is spelled under, in the config and on disk alike. */\nconst SERVICES = \"services\";\n\n/**\n * The root project's `exclude` list, or null when the file cannot be read as a\n * project config at all.\n *\n * The three answers are deliberately distinct, because two of them mean\n * opposite things to a grader:\n *\n * - `[]` — the file is a JSON object that excludes nothing (or names no\n * `exclude` key at all, which is the same thing to the renderer). Every\n * extending model is visible; a standalone one is in the root project where\n * it cannot parse.\n * - a list — the entries as written, in file order.\n * - `null` — absent, unreadable, not JSON, or JSON whose `exclude` is not a\n * list of strings. loam does not know what the renderer will do, so it says\n * nothing rather than guessing: the findings that read this list are silent\n * on null, `subsystem sync` leaves such a file alone, and the project loader\n * filters nothing. Failing closed here means the team keeps a file loam\n * cannot parse; failing open would mean loam asserting the renderer excludes\n * something on evidence it does not have.\n */\nexport async function readRootExclude(docsDir: DocsDir): Promise<string[] | null> {\n const parsed = await readRootProject(docsDir);\n if (parsed === null) return null;\n const exclude = parsed[\"exclude\"];\n if (exclude === undefined) return [];\n if (!Array.isArray(exclude)) return null;\n // A predicate rather than a cast, so the narrowing below is the compiler's\n // conclusion from a check that actually ran: an `exclude` holding a number is\n // a file loam does not understand, and understanding it half way is how a\n // grader ends up asserting a glob nobody wrote.\n const entries: unknown[] = exclude;\n return entries.every((entry): entry is string => typeof entry === \"string\") ? [...entries] : null;\n}\n\n/**\n * The root project file as a JSON object, or null when it is absent or is not\n * one — the ONE reader, shared with the one writer that re-parses it before a\n * rewrite (`commands/subsystem/txn/exclude.ts`).\n *\n * A LEADING BYTE-ORDER MARK IS STRIPPED, and that is not tidiness: PowerShell's\n * `Out-File` writes `ef bb bf` by default on this platform, so a Windows shell\n * saving the very file loam wrote left `JSON.parse` throwing while the renderer\n * — whose reader tolerates the mark — went on applying every entry in the list.\n * loam then graded nothing at all: `validate --all` silent about an exclusion in\n * full force, `doctor` healthy, `subsystem sync` reporting the list unreadable\n * and naming a fault (`not a JSON object with a string exclude list`) that was\n * not the one present (re-verification 2026-09-04, area C item 3). One reader\n * means the grade and the rewrite can never disagree about whether the file\n * parses.\n */\nasync function readRootProject(docsDir: DocsDir): Promise<Record<string, unknown> | null> {\n let text: string;\n try {\n text = await readFile(rootProjectPath(docsDir), \"utf8\");\n } catch {\n // Absent reads as \"loam cannot say\", not as \"excludes nothing\": a docs repo\n // with no root project file has no root project either, and the renderer\n // opened there shows nothing at all — a different problem, with a different\n // finding (`doctor.likec4-config-missing`).\n return null;\n }\n return parseRootProject(text);\n}\n\n/** The byte-order mark PowerShell's `Out-File` prepends by default on this platform. */\nconst BOM = \"\";\n\n/** The same parse, over bytes a caller already read. Exported for the rewrite path only. */\nexport function parseRootProject(text: string): Record<string, unknown> | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text.startsWith(BOM) ? text.slice(BOM.length) : text);\n } catch {\n // Not JSON is the team's file to fix, and nothing here may rewrite it.\n return null;\n }\n return isRecord(parsed) ? parsed : null;\n}\n\n/** A PATH as segments — `/` or `\\`, either spelling, no empty parts, no `.`. Never an ENTRY: see `rendererEntry`. */\nfunction segments(path: string): string[] {\n return path.split(/[\\\\/]/).filter((s) => s.length > 0 && s !== \".\");\n}\n\n/**\n * One `exclude` entry as the RENDERER will match it — the glob with the\n * spellings that mean nothing to it taken out — or null when the entry can hide\n * nothing loam is able to spell.\n *\n * WHY IT EXISTS. Every matcher below used to build its expression from the entry\n * exactly as written while normalising only the PATH, so four spellings of one\n * line disagreed with the renderer in loam's favour: `./architecture/palette.likec4`,\n * `architecture/./palette.likec4` and `architecture//palette.likec4` each hid the\n * palette from `likec4 validate` (8 source files, down from 9) and from nothing\n * in loam — the census loaded it, `subsystem sync` wrote a `global style\n * subsystems` line naming a group the fleet project cannot resolve, and the run\n * reported 0 errors while every render was Invalid. `./architecture/**` did the\n * same to the whole map with `landscape.excluded` silent (re-verification\n * 2026-09-04, area C items 1-2).\n *\n * MEASURED, at the 1.59.2 pin, against the renderer's own matcher — likec4\n * builds `withoutDoubleSlashes(joinRelativeURL(<project folder>, entry))` (a\n * doubled star and a slash joined in front unless the entry already leads with\n * one or is spelled relative) and hands it to picomatch with `contains: true,\n * dot: true`. The rules that fall out, each pinned in\n * `test/root-exclude.test.ts` against a `npx likec4 validate .` file count:\n *\n * - a leading `./` is resolved, and it ANCHORS the entry at the docs root —\n * the any-directories prefix is not added, so `./architecture/**` is about that one\n * directory and `architecture/**` is about one at any depth;\n * - `/./` and `//` inside the entry collapse (`architecture//palette.likec4`\n * hides the palette exactly as the plain spelling does);\n * - a TRAILING `/` does not: `architecture` hides `architecture-old/old.likec4`\n * as a raw prefix and `architecture/` does not (measured 5 files vs 6), so\n * the slash is kept rather than stripped;\n * - `.//` keeps a literal `.` directory in the pattern and matches NOTHING\n * (measured: 10 files of 10);\n * - a BACKSLASH is a literal character to picomatch, never a separator, so\n * `services\\order-service\\**` hides nothing at all (measured: 8 files of 8)\n * — loam used to read it as covering the tree, warn `service.model-excluded`\n * about a service the renderer loads perfectly well, and then DELETE the\n * team's line on the next `subsystem sync`.\n *\n * The two arms that answer null are the entries loam declines to reason about:\n * a backslash-bearing one (it covers nothing, so nothing may be graded on it)\n * and one whose `..` walks above the docs root, where the renderer's pattern\n * stops describing this tree at all.\n */\ninterface RendererEntry {\n /** The glob, `/`-separated, `.` segments resolved and `//` collapsed, any trailing `/` kept. */\n glob: string;\n /** True when a leading `./` anchored the entry at the docs root instead of at any depth. */\n anchored: boolean;\n}\n\nfunction rendererEntry(entry: string): RendererEntry | null {\n if (entry.includes(\"\\\\\")) return null;\n let rest = entry;\n let anchored = false;\n if (rest.startsWith(\"./\")) {\n rest = rest.slice(2);\n anchored = true;\n // `.//x` — the dot survives as a literal directory name, so the pattern\n // names a path nothing on disk has.\n if (rest.startsWith(\"/\")) return null;\n } else if (rest.startsWith(\"../\")) {\n return null;\n }\n const trailing = rest.endsWith(\"/\");\n const parts: string[] = [];\n for (const part of rest.split(\"/\")) {\n if (part.length === 0 || part === \".\") continue;\n if (part === \"..\") {\n if (parts.pop() === undefined) return null;\n continue;\n }\n parts.push(part);\n }\n // An entry that normalises to nothing at all is the project folder itself,\n // which `contains: true` makes a match for every document under it.\n const glob = parts.length === 0 ? \"**\" : `${parts.join(\"/\")}${trailing ? \"/\" : \"\"}`;\n return { glob, anchored: anchored && parts.length > 0 };\n}\n\n/** The expression one entry matches with, or null when it matches nothing — `rendererEntry` says which. */\nfunction matcherFor(parsed: RendererEntry, glob: string): RegExp {\n return new RegExp(`^${globSource(parsed.anchored ? glob : prefixed(glob))}`);\n}\n\n/**\n * An entry's segments with the trailing bare wildcards taken off — the question\n * \"which directory is this line ABOUT\", asked of the four spellings that name\n * one: `services/pay` doubled-star, that entry with a trailing `/*` after it,\n * `services/pay/*` and `services/pay/` all reduce to `services/pay`. A wildcard\n * that is NOT trailing survives — one segment of its own — because a starred\n * middle segment picks one directory level and no more.\n *\n * It is NOT the covering rule. What an entry HIDES is `entryRegex` below, and\n * the two answer differently on purpose: `services/pay/**` is about the\n * directory `services/pay`, while the bare `services/pay` hides\n * `services/payment-service` as well (measured — see below).\n *\n * It takes the NORMALISED glob, never the raw entry: an entry is split on `/`\n * alone, because a backslash is a literal to the renderer and splitting on it\n * here was what made `services\\order-service\\**` read as naming a directory it\n * hides nothing in.\n */\nfunction patternSegments(glob: string): string[] {\n const parts = glob.split(\"/\").filter((part) => part.length > 0);\n while (parts.length > 0 && (parts.at(-1) === \"*\" || parts.at(-1) === \"**\")) parts.pop();\n return parts;\n}\n\n/**\n * One `exclude` entry as the expression the RENDERER matches paths with:\n * anchored at the start of a path segment, and NOT anchored at the end.\n *\n * MEASURED, at the 1.59.2 pin, on a scratch fleet of five documents —\n * `architecture/landscape.likec4`, `architecture/usecases/uc.likec4`,\n * `services/payment-service/model.likec4`,\n * `services/payments/payment-service/model.likec4` and\n * `services/order-service/model.likec4` — counting the \"N files\" `npx likec4\n * validate .` reports with one entry in the list:\n *\n * services/zzz 5 services/pay 3\n * services/payment-service 4 a star as the middle segment 4\n * services/pay/** 5 architect 3\n * architecture/*.likec4 4 *.likec4 0\n * ervices 5 ces/payment-service 5\n * payment-service 3 services/** 2\n * services 2 services/payment-service/* hides a nested file too\n *\n * Four rules come out of that table, and every one of them contradicts what\n * this module asserted before (verification 2026-09-04, review C): the match is\n * a raw PREFIX with no end anchor (`services/pay` takes both payment trees,\n * `architect` takes all of `architecture/`); it must START on a `/` boundary\n * (`ervices` and `ces/payment-service` take nothing); `*` stays inside one\n * segment (`architecture/*.likec4` leaves `architecture/usecases/uc.likec4`);\n * and a glob form does NOT prefix-match (`services/pay/**` needs the literal\n * separator, so it takes nothing from `services/payment-service`).\n *\n * The shape falls out of what LikeC4 does with the list: an entry that does not\n * already begin with a doubled star is prefixed with one, joined onto the\n * project folder and handed to picomatch with `contains: true` — and `contains`\n * is exactly the missing end anchor. So the optional any-directories group\n * `globSource` emits for a leading doubled star IS that prefix, and the absent\n * `$` is that option.\n *\n * NULL when the entry hides nothing at all — `rendererEntry` names the two\n * spellings that do, and every caller has to skip such an entry rather than\n * treat it as covering: a warning derived from one sent a team after a line the\n * renderer ignores, and `subsystem sync` then deleted it.\n */\nfunction entryRegex(entry: string): RegExp | null {\n const parsed = rendererEntry(entry);\n return parsed === null ? null : matcherFor(parsed, parsed.glob);\n}\n\n/** An entry as LikeC4 spells it before matching: a doubled star in front, unless it has one. */\nfunction prefixed(entry: string): string {\n return entry.startsWith(\"**\") ? entry : `**/${entry}`;\n}\n\n/**\n * A glob as a regular-expression SOURCE. `**` crosses `/` and, followed by a\n * separator, may stand for no directory at all (a doubled star between two literals matches with no directory between them, which\n * is what makes a doubled-star-then-star entry cover a model directly under `services/`);\n * `*` and `?` never cross one.\n */\nfunction globSource(pattern: string): string {\n let source = \"\";\n let i = 0;\n while (i < pattern.length) {\n if (pattern.startsWith(\"**/\", i)) {\n source += \"(?:.*/)?\";\n i += 3;\n continue;\n }\n if (pattern.startsWith(\"**\", i)) {\n source += \".*\";\n i += 2;\n continue;\n }\n const c = pattern[i]!;\n if (c === \"*\") source += \"[^/]*\";\n else if (c === \"?\") source += \"[^/]\";\n else source += c.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n i += 1;\n }\n return source;\n}\n\n/**\n * The entry that hides `relPath` from the root project, or null when nothing in\n * the list does. `relPath` is a docs-relative FILE path, spelled either way\n * round the slash.\n *\n * Returned VERBATIM because every message that carries it asks its reader to\n * delete or narrow THAT line, and an entry re-spelled by loam is a line they\n * would go looking for and not find.\n */\nexport function excludingPath(exclude: readonly string[], relPath: string): string | null {\n const path = segments(relPath).join(\"/\");\n if (path.length === 0) return null;\n for (const entry of exclude) {\n if (entryRegex(entry)?.test(path) === true) return entry;\n }\n return null;\n}\n\n/**\n * The entry that hides a SERVICE TREE — the directory and everything under it —\n * or null. The tree's spelling is normalised here because the repository writes\n * it both ways: `serviceTreePath` includes the leading `services/` and the\n * entries `standaloneExclude` writes are built from the part under it, and\n * getting that wrong would silently answer \"not excluded\" for every service.\n *\n * A DIRECTORY IS NOT A FILE, and that is why this is not `excludingPath` on the\n * directory's own path. `services/payment-service/**` hides every document in\n * that tree while matching no prefix of the directory string itself — the\n * pattern wants the separator the directory path ends before. So a second arm\n * asks the other half: does the directory the entry NAMES (`patternSegments`,\n * the trailing wildcards off) contain this tree? Asking it of the named\n * directory rather than of the raw prefix is what keeps `services/pay/**` about\n * `services/pay` and away from `services/payment-service`, which is exactly\n * where the renderer leaves it.\n *\n * What this deliberately does NOT report is an entry that hides only PART of a\n * tree (`services/x/model`, say). The four findings that read this answer are\n * about a directory the renderer either loads or does not, and half of one is a\n * state `subsystem sync` has no entry to write for.\n */\nexport function excludingEntry(exclude: readonly string[], treePath: string): string | null {\n const tree = segments(treePath).join(\"/\");\n if (tree.length === 0) return null;\n const full = tree === SERVICES || tree.startsWith(`${SERVICES}/`) ? tree : `${SERVICES}/${tree}`;\n for (const entry of exclude) {\n if (coversTree(entry, full)) return entry;\n }\n return null;\n}\n\n/** Does one entry hide `dir` and everything under it? The two arms `excludingEntry` documents. */\nfunction coversTree(entry: string, dir: string): boolean {\n const parsed = rendererEntry(entry);\n // An entry that hides nothing covers nothing — the backslash spelling, which\n // loam used to read as covering the tree while the renderer loaded every file\n // in it (measured: 8 of 8), warning `service.model-excluded` about a healthy\n // service and then deleting the team's line on the next sync.\n if (parsed === null) return false;\n if (matcherFor(parsed, parsed.glob).test(dir)) return true;\n const named = patternSegments(parsed.glob);\n if (named.length === 0) return false;\n // The named directory, or an ancestor of it, matched on a segment boundary —\n // never mid-segment, which is the whole difference between this arm and the\n // prefix one above.\n return new RegExp(`${matcherFor(parsed, named.join(\"/\")).source}(?:/|$)`).test(dir);\n}\n\n/**\n * The DIRECTORY an entry names, `/`-joined, or null when the entry does not\n * plainly name one — a wildcard segment between two literal ones, or the\n * leading doubled star of the `node_modules` glob.\n *\n * The trailing wildcards are what get stripped: `services/pay` doubled-star and\n * the three other spellings `patternSegments` lists all name `services/pay`. It\n * is the AUTHORSHIP question the writer asks — \"did loam write this line, and is\n * it about a directory the enumeration returns?\" — never the covering one, which\n * is `excludingEntry`'s.\n */\nexport function excludedDirectory(entry: string): string | null {\n const parsed = rendererEntry(entry);\n // An entry that hides nothing names nothing either, so a backslash spelling is\n // never loam's to rewrite: `subsystem sync` leaves it exactly where the team\n // put it, which is the only honest answer about a line the renderer ignores.\n if (parsed === null) return null;\n const parts = patternSegments(parsed.glob);\n return parts.some((part) => part.includes(\"*\")) ? null : parts.join(\"/\");\n}\n"]}
@@ -22,6 +22,20 @@
22
22
  function foldName(name, kind) {
23
23
  return `${kind}_${name.replace(/[.-]/g, "_")}`;
24
24
  }
25
+ /**
26
+ * The element id loam SUGGESTS for a service no map draws yet — this seed's
27
+ * own convention, exported so it is the only one. The adopt brief and the
28
+ * `loam new` delta used to camel-case the directory name instead, so a seeded
29
+ * fleet received three spellings of one box (`svc_payment_service` on the
30
+ * map, `paymentService` in every brief and scaffold), and a delta that reused
31
+ * the scaffold's guess never lined up with the map it was told to match. The
32
+ * id stays cosmetic — `metadata { service '<id>' }` is the join — but a
33
+ * suggestion gets pasted, so it has to be one spelling, and the keyword-proof
34
+ * one above is the one already on disk in every seeded repo.
35
+ */
36
+ export function serviceElementId(service) {
37
+ return foldName(service, "svc");
38
+ }
25
39
  /** Element ids for every declared name (services ∪ externals), collision-suffixed deterministically. */
26
40
  function elementIds(seed) {
27
41
  const ids = new Map();
@@ -57,7 +71,7 @@ export function renderLandscape(seed) {
57
71
  "// As contracts arrive, give each call the operationId it uses, so",
58
72
  "// requirements, C4 and OpenAPI can be cross-checked:",
59
73
  "//",
60
- "// checkout -> paymentService 'Authorizes' {",
74
+ "// svc_checkout -> svc_payment_service 'Calls authorizePayment' {",
61
75
  "// metadata { op 'authorizePayment' }",
62
76
  "// }",
63
77
  "",
@@ -73,6 +87,56 @@ export function renderLandscape(seed) {
73
87
  " // used, so both are declared here whether or not this fleet uses them.",
74
88
  " tag platform",
75
89
  " tag external",
90
+ // The relationship kinds are DUPLICATED from core/scaffold/landscape.ts's
91
+ // LANDSCAPE_STUB for the views block's reason below, and test/seed.test.ts
92
+ // compares the two byte for byte the same way. Declared, never used: the
93
+ // calls below stay plain edges, because fleet.yaml says who calls whom and
94
+ // has no spelling for what an edge carries — choosing a kind here would be
95
+ // the guess this templater exists not to make. Declaring them is what lets
96
+ // the adoptions and feature deltas that follow draw kinded edges into this
97
+ // map, and lets an archive that copies one in still parse.
98
+ "",
99
+ " // One kind per kind of traffic, each with one direction. The style is the",
100
+ " // picture: amber for messages, green for data at rest, gray and dashed for",
101
+ " // a connection nobody has given a direction yet. Never tag a kind: LikeC4",
102
+ " // copies a kind's tags onto every edge of it.",
103
+ " relationship publishes {",
104
+ " color amber",
105
+ " line solid",
106
+ " head normal",
107
+ " }",
108
+ " relationship delivers {",
109
+ " color amber",
110
+ " line solid",
111
+ " head normal",
112
+ " }",
113
+ " relationship reads {",
114
+ " color green",
115
+ " head none",
116
+ " tail normal",
117
+ " }",
118
+ " relationship writes {",
119
+ " color green",
120
+ " head normal",
121
+ " }",
122
+ " relationship readsWrites {",
123
+ " color green",
124
+ " head normal",
125
+ " tail normal",
126
+ " }",
127
+ " relationship calls {",
128
+ " color secondary",
129
+ " line solid",
130
+ " head normal",
131
+ " }",
132
+ " relationship connects {",
133
+ " color gray",
134
+ " line dashed",
135
+ " head none",
136
+ " // A marker at the source: an edge with no arrowhead at either end is laid",
137
+ " // out unconstrained, and enough of them flatten a view into one band.",
138
+ " tail odot",
139
+ " }",
76
140
  "}",
77
141
  "",
78
142
  "model {",
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/template.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FleetSeed → landscape DSL body: a pure, deterministic function — identical\n * fleet.yaml, identical bytes, on any machine — because the stamp\n * (`./stamp.ts`) digests the output and a nondeterministic templater would\n * read as hand-edited on the next run.\n *\n * The DSL shape is deliberately minimal: one bound `softwareSystem` per\n * service (the `metadata { service '<id>' }` binding is what every loam join\n * reads — element ids are cosmetic), `#external` systems for the externals,\n * and PLAIN edges. No operationIds: those arrive with contracts, and a\n * templated guess would be the extractor this command exists not to be.\n */\nimport type { FleetSeed } from \"./fleet-file.js\";\n\n/** Which kind of declared name an element id is being built for. */\ntype ElementKind = \"svc\" | \"ext\";\n\n/**\n * A LikeC4 identifier for a declared name, mechanically: `.` and `-` become\n * `_`, and the result takes its kind's prefix — `svc_` for a service, `ext_`\n * for an external.\n *\n * The prefix is not decoration, and not only about the digit-initial case\n * (LikeC4 identifiers are letter/underscore-initial). LikeC4's KEYWORDS are\n * bare lowercase words — `notes`, `tag`, `view`, `metadata`, `style`, `link`,\n * `import`, `description`, `size`, `line`, `this` and some thirty more — and a\n * service legally named `notes` emitted as a bare `notes = softwareSystem`\n * does not parse. The self-check would catch that, but only to refuse a fleet\n * whose fault it is not, under `internal`, with no fix to offer. No keyword\n * contains `_`, so a prefixed identifier is PROVABLY not one — at this LikeC4\n * version and at every later one. A reserved-word list here would be the same\n * bug again, rotting the first time the language grew a word.\n *\n * The fold is not injective — `a-b` and `a.b` both give `a_b` — so residual\n * collisions take a deterministic numeric suffix in sorted processing order.\n * Aesthetics do not matter here: the binding metadata, not the element id, is\n * the element's identity to loam.\n */\nfunction foldName(name: string, kind: ElementKind): string {\n return `${kind}_${name.replace(/[.-]/g, \"_\")}`;\n}\n\n/** Element ids for every declared name (services ∪ externals), collision-suffixed deterministically. */\nfunction elementIds(seed: FleetSeed): Map<string, string> {\n const ids = new Map<string, string>();\n const used = new Set<string>();\n const named: Array<readonly [string, ElementKind]> = [\n ...[...seed.services]\n .map((s) => s.id as string)\n .sort()\n .map((n) => [n, \"svc\"] as const),\n ...[...seed.externals].sort().map((n) => [n, \"ext\"] as const),\n ];\n for (const [name, kind] of named) {\n let candidate = foldName(name, kind);\n for (let n = 2; used.has(candidate); n += 1) candidate = `${foldName(name, kind)}_${n}`;\n used.add(candidate);\n ids.set(name, candidate);\n }\n return ids;\n}\n\n/** Render the landscape body (unstamped — `sealLandscape` adds line 1). */\nexport function renderLandscape(seed: FleetSeed): string {\n const ids = elementIds(seed);\n // Every name reaching `el` was declared (calls are validated against\n // services ∪ externals before rendering), so the fallback is unreachable —\n // it exists so a future caller cannot get a silent `undefined` into the DSL.\n const el = (name: string): string => ids.get(name) ?? foldName(name, \"svc\");\n\n const lines: string[] = [\n \"// Generated from fleet.yaml by `loam seed` — edit fleet.yaml and re-run to\",\n \"// regenerate. Hand edits make this file yours: the stamp on line 1 is how\",\n \"// seed tells, and it will then refuse to touch the file again.\",\n \"//\",\n \"// As contracts arrive, give each call the operationId it uses, so\",\n \"// requirements, C4 and OpenAPI can be cross-checked:\",\n \"//\",\n \"// checkout -> paymentService 'Authorizes' {\",\n \"// metadata { op 'authorizePayment' }\",\n \"// }\",\n \"\",\n \"specification {\",\n \" element person\",\n \" element softwareSystem\",\n \" element container\",\n \" element database\",\n \"\",\n \" // #platform keeps ubiquitous infrastructure out of the fleet view;\",\n \" // #external marks somebody else's system, so validate does not ask for a\",\n \" // services/ directory nobody owes. A tag must be declared before it is\",\n \" // used, so both are declared here whether or not this fleet uses them.\",\n \" tag platform\",\n \" tag external\",\n \"}\",\n \"\",\n \"model {\",\n ];\n\n const services = [...seed.services].sort((a, b) => (a.id < b.id ? -1 : 1));\n for (const s of services) {\n lines.push(` ${el(s.id)} = softwareSystem '${s.id}' {`);\n lines.push(` metadata { service '${s.id}' }`);\n lines.push(\" }\");\n }\n const externals = [...seed.externals].sort();\n for (const x of externals) {\n lines.push(` ${el(x)} = softwareSystem '${x}' {`);\n lines.push(\" #external\");\n lines.push(\" }\");\n }\n const calls = [...seed.calls].sort((a, b) =>\n a.from === b.from ? (a.to < b.to ? -1 : 1) : a.from < b.from ? -1 : 1,\n );\n if (calls.length > 0) lines.push(\"\");\n for (const c of calls) lines.push(` ${el(c.from)} -> ${el(c.to)}`);\n lines.push(\"}\");\n\n // The views block below is DUPLICATED from core/docs.ts's LANDSCAPE_STUB\n // (two copies — extract on a third, per the house rule). The tripwire in\n // test/seed.test.ts compares the two blocks BYTE FOR BYTE, comments and\n // wrapping included, so whichever copy drifts fails the suite — a\n // view-names-only compare let a corrected predicate land in one copy and\n // leave every seeded fleet map rendering the old one.\n // The emitted comment speaks to the docs repo's reader, not to loam's:\n // internal paths mean nothing where this file lands.\n lines.push(\n \"\",\n \"// The views are for LikeC4's own tooling — loam reads the model and never\",\n \"// a view. Render with `npx likec4 start` from the docs repo root. Tag\",\n \"// ubiquitous infrastructure #platform to keep the fleet view readable.\",\n \"views {\",\n \" view fleet {\",\n \" include *\",\n \" exclude element.tag = #platform\",\n \" }\",\n \" // The obvious spelling — `include element.tag = #platform` — draws the\",\n \" // platform boxes with NO edges and no consumers. The predicate that works is\",\n \" // the relationship form below.\",\n \" view platform {\",\n \" include * -> element.tag = #platform\",\n \" }\",\n \"}\",\n );\n return lines.join(\"\\n\") + \"\\n\";\n}\n"]}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../src/core/c4/seed/template.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * FleetSeed → landscape DSL body: a pure, deterministic function — identical\n * fleet.yaml, identical bytes, on any machine — because the stamp\n * (`./stamp.ts`) digests the output and a nondeterministic templater would\n * read as hand-edited on the next run.\n *\n * The DSL shape is deliberately minimal: one bound `softwareSystem` per\n * service (the `metadata { service '<id>' }` binding is what every loam join\n * reads — element ids are cosmetic), `#external` systems for the externals,\n * and PLAIN edges. No operationIds: those arrive with contracts, and a\n * templated guess would be the extractor this command exists not to be. No\n * relationship kinds on the edges either, for the same reason — but the kinds\n * themselves are DECLARED, as the scaffold's stub declares them, so whatever\n * draws the next edge can name one.\n */\nimport type { FleetSeed } from \"./fleet-file.js\";\n\n/** Which kind of declared name an element id is being built for. */\ntype ElementKind = \"svc\" | \"ext\";\n\n/**\n * A LikeC4 identifier for a declared name, mechanically: `.` and `-` become\n * `_`, and the result takes its kind's prefix — `svc_` for a service, `ext_`\n * for an external.\n *\n * The prefix is not decoration, and not only about the digit-initial case\n * (LikeC4 identifiers are letter/underscore-initial). LikeC4's KEYWORDS are\n * bare lowercase words — `notes`, `tag`, `view`, `metadata`, `style`, `link`,\n * `import`, `description`, `size`, `line`, `this` and some thirty more — and a\n * service legally named `notes` emitted as a bare `notes = softwareSystem`\n * does not parse. The self-check would catch that, but only to refuse a fleet\n * whose fault it is not, under `internal`, with no fix to offer. No keyword\n * contains `_`, so a prefixed identifier is PROVABLY not one — at this LikeC4\n * version and at every later one. A reserved-word list here would be the same\n * bug again, rotting the first time the language grew a word.\n *\n * The fold is not injective — `a-b` and `a.b` both give `a_b` — so residual\n * collisions take a deterministic numeric suffix in sorted processing order.\n * Aesthetics do not matter here: the binding metadata, not the element id, is\n * the element's identity to loam.\n */\nfunction foldName(name: string, kind: ElementKind): string {\n return `${kind}_${name.replace(/[.-]/g, \"_\")}`;\n}\n\n/**\n * The element id loam SUGGESTS for a service no map draws yet — this seed's\n * own convention, exported so it is the only one. The adopt brief and the\n * `loam new` delta used to camel-case the directory name instead, so a seeded\n * fleet received three spellings of one box (`svc_payment_service` on the\n * map, `paymentService` in every brief and scaffold), and a delta that reused\n * the scaffold's guess never lined up with the map it was told to match. The\n * id stays cosmetic — `metadata { service '<id>' }` is the join — but a\n * suggestion gets pasted, so it has to be one spelling, and the keyword-proof\n * one above is the one already on disk in every seeded repo.\n */\nexport function serviceElementId(service: string): string {\n return foldName(service, \"svc\");\n}\n\n/** Element ids for every declared name (services ∪ externals), collision-suffixed deterministically. */\nfunction elementIds(seed: FleetSeed): Map<string, string> {\n const ids = new Map<string, string>();\n const used = new Set<string>();\n const named: Array<readonly [string, ElementKind]> = [\n ...[...seed.services]\n .map((s) => s.id as string)\n .sort()\n .map((n) => [n, \"svc\"] as const),\n ...[...seed.externals].sort().map((n) => [n, \"ext\"] as const),\n ];\n for (const [name, kind] of named) {\n let candidate = foldName(name, kind);\n for (let n = 2; used.has(candidate); n += 1) candidate = `${foldName(name, kind)}_${n}`;\n used.add(candidate);\n ids.set(name, candidate);\n }\n return ids;\n}\n\n/** Render the landscape body (unstamped — `sealLandscape` adds line 1). */\nexport function renderLandscape(seed: FleetSeed): string {\n const ids = elementIds(seed);\n // Every name reaching `el` was declared (calls are validated against\n // services ∪ externals before rendering), so the fallback is unreachable —\n // it exists so a future caller cannot get a silent `undefined` into the DSL.\n const el = (name: string): string => ids.get(name) ?? foldName(name, \"svc\");\n\n const lines: string[] = [\n \"// Generated from fleet.yaml by `loam seed` — edit fleet.yaml and re-run to\",\n \"// regenerate. Hand edits make this file yours: the stamp on line 1 is how\",\n \"// seed tells, and it will then refuse to touch the file again.\",\n \"//\",\n \"// As contracts arrive, give each call the operationId it uses, so\",\n \"// requirements, C4 and OpenAPI can be cross-checked:\",\n \"//\",\n \"// svc_checkout -> svc_payment_service 'Calls authorizePayment' {\",\n \"// metadata { op 'authorizePayment' }\",\n \"// }\",\n \"\",\n \"specification {\",\n \" element person\",\n \" element softwareSystem\",\n \" element container\",\n \" element database\",\n \"\",\n \" // #platform keeps ubiquitous infrastructure out of the fleet view;\",\n \" // #external marks somebody else's system, so validate does not ask for a\",\n \" // services/ directory nobody owes. A tag must be declared before it is\",\n \" // used, so both are declared here whether or not this fleet uses them.\",\n \" tag platform\",\n \" tag external\",\n // The relationship kinds are DUPLICATED from core/scaffold/landscape.ts's\n // LANDSCAPE_STUB for the views block's reason below, and test/seed.test.ts\n // compares the two byte for byte the same way. Declared, never used: the\n // calls below stay plain edges, because fleet.yaml says who calls whom and\n // has no spelling for what an edge carries — choosing a kind here would be\n // the guess this templater exists not to make. Declaring them is what lets\n // the adoptions and feature deltas that follow draw kinded edges into this\n // map, and lets an archive that copies one in still parse.\n \"\",\n \" // One kind per kind of traffic, each with one direction. The style is the\",\n \" // picture: amber for messages, green for data at rest, gray and dashed for\",\n \" // a connection nobody has given a direction yet. Never tag a kind: LikeC4\",\n \" // copies a kind's tags onto every edge of it.\",\n \" relationship publishes {\",\n \" color amber\",\n \" line solid\",\n \" head normal\",\n \" }\",\n \" relationship delivers {\",\n \" color amber\",\n \" line solid\",\n \" head normal\",\n \" }\",\n \" relationship reads {\",\n \" color green\",\n \" head none\",\n \" tail normal\",\n \" }\",\n \" relationship writes {\",\n \" color green\",\n \" head normal\",\n \" }\",\n \" relationship readsWrites {\",\n \" color green\",\n \" head normal\",\n \" tail normal\",\n \" }\",\n \" relationship calls {\",\n \" color secondary\",\n \" line solid\",\n \" head normal\",\n \" }\",\n \" relationship connects {\",\n \" color gray\",\n \" line dashed\",\n \" head none\",\n \" // A marker at the source: an edge with no arrowhead at either end is laid\",\n \" // out unconstrained, and enough of them flatten a view into one band.\",\n \" tail odot\",\n \" }\",\n \"}\",\n \"\",\n \"model {\",\n ];\n\n const services = [...seed.services].sort((a, b) => (a.id < b.id ? -1 : 1));\n for (const s of services) {\n lines.push(` ${el(s.id)} = softwareSystem '${s.id}' {`);\n lines.push(` metadata { service '${s.id}' }`);\n lines.push(\" }\");\n }\n const externals = [...seed.externals].sort();\n for (const x of externals) {\n lines.push(` ${el(x)} = softwareSystem '${x}' {`);\n lines.push(\" #external\");\n lines.push(\" }\");\n }\n const calls = [...seed.calls].sort((a, b) =>\n a.from === b.from ? (a.to < b.to ? -1 : 1) : a.from < b.from ? -1 : 1,\n );\n if (calls.length > 0) lines.push(\"\");\n for (const c of calls) lines.push(` ${el(c.from)} -> ${el(c.to)}`);\n lines.push(\"}\");\n\n // The views block below is DUPLICATED from core/docs.ts's LANDSCAPE_STUB\n // (two copies — extract on a third, per the house rule). The tripwire in\n // test/seed.test.ts compares the two blocks BYTE FOR BYTE, comments and\n // wrapping included, so whichever copy drifts fails the suite — a\n // view-names-only compare let a corrected predicate land in one copy and\n // leave every seeded fleet map rendering the old one.\n // The emitted comment speaks to the docs repo's reader, not to loam's:\n // internal paths mean nothing where this file lands.\n lines.push(\n \"\",\n \"// The views are for LikeC4's own tooling — loam reads the model and never\",\n \"// a view. Render with `npx likec4 start` from the docs repo root. Tag\",\n \"// ubiquitous infrastructure #platform to keep the fleet view readable.\",\n \"views {\",\n \" view fleet {\",\n \" include *\",\n \" exclude element.tag = #platform\",\n \" }\",\n \" // The obvious spelling — `include element.tag = #platform` — draws the\",\n \" // platform boxes with NO edges and no consumers. The predicate that works is\",\n \" // the relationship form below.\",\n \" view platform {\",\n \" include * -> element.tag = #platform\",\n \" }\",\n \"}\",\n );\n return lines.join(\"\\n\") + \"\\n\";\n}\n"]}