@ybotok/loam 0.2.0-alpha.7 → 0.2.0-alpha.9

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 (648) hide show
  1. package/CHANGELOG.md +1315 -1
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +40 -22
  5. package/QUICKSTART.md +181 -9
  6. package/README.md +256 -57
  7. package/ROADMAP.md +83 -7
  8. package/SCHEMA.md +569 -98
  9. package/WORKFLOW.md +272 -62
  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/archive/archive.js +15 -18
  15. package/dist/commands/archive/archive.js.map +1 -1
  16. package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
  17. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  18. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  19. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/openapi.js +25 -42
  21. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
  23. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  24. package/dist/commands/archive/plan/gate.js +3 -7
  25. package/dist/commands/archive/plan/gate.js.map +1 -1
  26. package/dist/commands/archive/plan/landscape.js +4 -4
  27. package/dist/commands/archive/plan/landscape.js.map +1 -1
  28. package/dist/commands/archive/plan/refusal.js +8 -18
  29. package/dist/commands/archive/plan/refusal.js.map +1 -1
  30. package/dist/commands/archive/plan/specs.js +134 -25
  31. package/dist/commands/archive/plan/specs.js.map +1 -1
  32. package/dist/commands/archive/plan/state.js +1 -0
  33. package/dist/commands/archive/plan/state.js.map +1 -1
  34. package/dist/commands/archive/run.js +33 -11
  35. package/dist/commands/archive/run.js.map +1 -1
  36. package/dist/commands/archive/shipped.js +49 -0
  37. package/dist/commands/archive/shipped.js.map +1 -0
  38. package/dist/commands/context/context.js +4 -7
  39. package/dist/commands/context/context.js.map +1 -1
  40. package/dist/commands/context/print.js +2 -2
  41. package/dist/commands/context/print.js.map +1 -1
  42. package/dist/commands/delta/delta.js +44 -12
  43. package/dist/commands/delta/delta.js.map +1 -1
  44. package/dist/commands/delta/print.js +1 -1
  45. package/dist/commands/delta/print.js.map +1 -1
  46. package/dist/commands/dependencies.js +3 -7
  47. package/dist/commands/dependencies.js.map +1 -1
  48. package/dist/commands/diff/diff.js +20 -27
  49. package/dist/commands/diff/diff.js.map +1 -1
  50. package/dist/commands/explore.js +4 -8
  51. package/dist/commands/explore.js.map +1 -1
  52. package/dist/commands/gate/gate.js +45 -8
  53. package/dist/commands/gate/gate.js.map +1 -1
  54. package/dist/commands/gate/print.js +15 -3
  55. package/dist/commands/gate/print.js.map +1 -1
  56. package/dist/commands/gherkin/commit.js +13 -54
  57. package/dist/commands/gherkin/commit.js.map +1 -1
  58. package/dist/commands/gherkin/gherkin.js +36 -20
  59. package/dist/commands/gherkin/gherkin.js.map +1 -1
  60. package/dist/commands/gherkin/reconcile.js +10 -13
  61. package/dist/commands/gherkin/reconcile.js.map +1 -1
  62. package/dist/commands/init/first-hour.js +44 -0
  63. package/dist/commands/init/first-hour.js.map +1 -1
  64. package/dist/commands/init/init.js +18 -22
  65. package/dist/commands/init/init.js.map +1 -1
  66. package/dist/commands/list/campaign/campaign.js +4 -6
  67. package/dist/commands/list/campaign/campaign.js.map +1 -1
  68. package/dist/commands/list/json.js +0 -4
  69. package/dist/commands/list/json.js.map +1 -1
  70. package/dist/commands/list/list.js +38 -23
  71. package/dist/commands/list/list.js.map +1 -1
  72. package/dist/commands/list/print.js +38 -6
  73. package/dist/commands/list/print.js.map +1 -1
  74. package/dist/commands/list/suites/suites.js +140 -0
  75. package/dist/commands/list/suites/suites.js.map +1 -0
  76. package/dist/commands/list/views.js +28 -33
  77. package/dist/commands/list/views.js.map +1 -1
  78. package/dist/commands/mcp/dispatch.js +91 -36
  79. package/dist/commands/mcp/dispatch.js.map +1 -1
  80. package/dist/commands/mcp/mcp.js +56 -15
  81. package/dist/commands/mcp/mcp.js.map +1 -1
  82. package/dist/commands/mcp/serve.js.map +1 -1
  83. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  84. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  85. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  86. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  87. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  88. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  89. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  90. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  91. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  92. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  93. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  94. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  95. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  96. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  97. package/dist/commands/new/capability-template.js +118 -0
  98. package/dist/commands/new/capability-template.js.map +1 -0
  99. package/dist/commands/new/commit.js +8 -22
  100. package/dist/commands/new/commit.js.map +1 -1
  101. package/dist/commands/new/modifies.js +206 -0
  102. package/dist/commands/new/modifies.js.map +1 -0
  103. package/dist/commands/new/new.js +59 -14
  104. package/dist/commands/new/new.js.map +1 -1
  105. package/dist/commands/new/templates.js +48 -124
  106. package/dist/commands/new/templates.js.map +1 -1
  107. package/dist/commands/open.js +3 -7
  108. package/dist/commands/open.js.map +1 -1
  109. package/dist/commands/policy/gate.js +27 -0
  110. package/dist/commands/policy/gate.js.map +1 -1
  111. package/dist/commands/rebase/plan.js +2 -2
  112. package/dist/commands/rebase/plan.js.map +1 -1
  113. package/dist/commands/rebase/rebase.js +60 -35
  114. package/dist/commands/rebase/rebase.js.map +1 -1
  115. package/dist/commands/seed/commit.js +21 -25
  116. package/dist/commands/seed/commit.js.map +1 -1
  117. package/dist/commands/seed/seed.js +6 -9
  118. package/dist/commands/seed/seed.js.map +1 -1
  119. package/dist/commands/show/show.js +3 -7
  120. package/dist/commands/show/show.js.map +1 -1
  121. package/dist/commands/status/print.js +13 -4
  122. package/dist/commands/status/print.js.map +1 -1
  123. package/dist/commands/status/status.js +3 -7
  124. package/dist/commands/status/status.js.map +1 -1
  125. package/dist/commands/steps/steps.js +3 -7
  126. package/dist/commands/steps/steps.js.map +1 -1
  127. package/dist/commands/subsystem/edit.js +3 -5
  128. package/dist/commands/subsystem/edit.js.map +1 -1
  129. package/dist/commands/subsystem/move.js +5 -8
  130. package/dist/commands/subsystem/move.js.map +1 -1
  131. package/dist/commands/subsystem/query.js +2 -4
  132. package/dist/commands/subsystem/query.js.map +1 -1
  133. package/dist/commands/subsystem/subsystem.js +4 -7
  134. package/dist/commands/subsystem/subsystem.js.map +1 -1
  135. package/dist/commands/subsystem/sync.js +14 -41
  136. package/dist/commands/subsystem/sync.js.map +1 -1
  137. package/dist/commands/subsystem/txn/txn.js +17 -38
  138. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  139. package/dist/commands/subsystem/txn/views.js +32 -5
  140. package/dist/commands/subsystem/txn/views.js.map +1 -1
  141. package/dist/commands/tasks/render.js +116 -0
  142. package/dist/commands/tasks/render.js.map +1 -0
  143. package/dist/commands/tasks/tasks.js +113 -0
  144. package/dist/commands/tasks/tasks.js.map +1 -0
  145. package/dist/commands/unarchive/manifest.js +6 -9
  146. package/dist/commands/unarchive/manifest.js.map +1 -1
  147. package/dist/commands/unarchive/restore.js +17 -28
  148. package/dist/commands/unarchive/restore.js.map +1 -1
  149. package/dist/commands/unarchive/unarchive.js +22 -30
  150. package/dist/commands/unarchive/unarchive.js.map +1 -1
  151. package/dist/commands/validate/arch-coverage.js +6 -15
  152. package/dist/commands/validate/arch-coverage.js.map +1 -1
  153. package/dist/commands/validate/checks/fleet-shape.js +4 -49
  154. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  155. package/dist/commands/validate/checks/requirements.js +18 -8
  156. package/dist/commands/validate/checks/requirements.js.map +1 -1
  157. package/dist/commands/validate/checks/sources.js +6 -2
  158. package/dist/commands/validate/checks/sources.js.map +1 -1
  159. package/dist/commands/validate/feature.js +23 -19
  160. package/dist/commands/validate/feature.js.map +1 -1
  161. package/dist/commands/validate/fleet/landscape.js +23 -23
  162. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  163. package/dist/commands/validate/fleet/map/attest.js +6 -10
  164. package/dist/commands/validate/fleet/map/attest.js.map +1 -1
  165. package/dist/commands/validate/fleet/map/bindings.js +1 -3
  166. package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
  167. package/dist/commands/validate/fleet/map/usecases.js +5 -11
  168. package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
  169. package/dist/commands/validate/fleet/obligations.js +1 -5
  170. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  171. package/dist/commands/validate/fleet/scorecard/print.js +5 -1
  172. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  173. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  174. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  175. package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
  176. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
  177. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
  178. package/dist/commands/validate/fleet/views/root-documents.js +8 -28
  179. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
  180. package/dist/commands/validate/links/corpus.js +1 -4
  181. package/dist/commands/validate/links/corpus.js.map +1 -1
  182. package/dist/commands/validate/links/glossary.js.map +1 -1
  183. package/dist/commands/validate/report.js +5 -4
  184. package/dist/commands/validate/report.js.map +1 -1
  185. package/dist/commands/validate/scope.js +113 -0
  186. package/dist/commands/validate/scope.js.map +1 -0
  187. package/dist/commands/validate/service/api.js +16 -104
  188. package/dist/commands/validate/service/api.js.map +1 -1
  189. package/dist/commands/validate/service/contracts/generated.js +134 -0
  190. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  191. package/dist/commands/validate/service/events/events.js +1 -1
  192. package/dist/commands/validate/service/events/events.js.map +1 -1
  193. package/dist/commands/validate/service/events/message-producers.js +1 -1
  194. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  195. package/dist/commands/validate/service/evidence-pins.js +56 -10
  196. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  197. package/dist/commands/validate/service/service.js +16 -26
  198. package/dist/commands/validate/service/service.js.map +1 -1
  199. package/dist/commands/validate/service/specs.js +31 -38
  200. package/dist/commands/validate/service/specs.js.map +1 -1
  201. package/dist/commands/validate/service/spine.js +8 -11
  202. package/dist/commands/validate/service/spine.js.map +1 -1
  203. package/dist/commands/validate/service/usecases/usecases.js +3 -3
  204. package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
  205. package/dist/commands/validate/validate.js +44 -26
  206. package/dist/commands/validate/validate.js.map +1 -1
  207. package/dist/commands/verify/cross/diff.js +2 -6
  208. package/dist/commands/verify/cross/diff.js.map +1 -1
  209. package/dist/commands/verify/evidence/contract.js +1 -1
  210. package/dist/commands/verify/evidence/contract.js.map +1 -1
  211. package/dist/commands/verify/evidence/read.js +12 -3
  212. package/dist/commands/verify/evidence/read.js.map +1 -1
  213. package/dist/commands/verify/frozen.js +45 -23
  214. package/dist/commands/verify/frozen.js.map +1 -1
  215. package/dist/commands/verify/record.js +69 -17
  216. package/dist/commands/verify/record.js.map +1 -1
  217. package/dist/commands/verify/report.js +27 -44
  218. package/dist/commands/verify/report.js.map +1 -1
  219. package/dist/commands/verify/results.js +112 -30
  220. package/dist/commands/verify/results.js.map +1 -1
  221. package/dist/commands/verify/verify.js +54 -51
  222. package/dist/commands/verify/verify.js.map +1 -1
  223. package/dist/commands/vouch/pack/pack.js +2 -2
  224. package/dist/commands/vouch/pack/pack.js.map +1 -1
  225. package/dist/commands/vouch/pack/print.js +1 -6
  226. package/dist/commands/vouch/pack/print.js.map +1 -1
  227. package/dist/commands/vouch/pack/sections.js +1 -2
  228. package/dist/commands/vouch/pack/sections.js.map +1 -1
  229. package/dist/commands/vouch/run.js +26 -81
  230. package/dist/commands/vouch/run.js.map +1 -1
  231. package/dist/commands/vouch/sample/plan.js +20 -34
  232. package/dist/commands/vouch/sample/plan.js.map +1 -1
  233. package/dist/commands/vouch/vet/verify.js +39 -7
  234. package/dist/commands/vouch/vet/verify.js.map +1 -1
  235. package/dist/commands/vouch/vouch.js +10 -17
  236. package/dist/commands/vouch/vouch.js.map +1 -1
  237. package/dist/core/agent/agents-md/command-map.js +56 -101
  238. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  239. package/dist/core/agent/agents-md/map/explain.js +4 -3
  240. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  241. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  242. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  243. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  244. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  245. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  246. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  247. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  248. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  249. package/dist/core/agent/agents-md/map/status.js +9 -3
  250. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  251. package/dist/core/agent/agents-md/map/subsystem.js +4 -3
  252. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  253. package/dist/core/agent/agents-md/map/usecases.js +24 -6
  254. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  255. package/dist/core/agent/agents-md/refusals.js +32 -18
  256. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  257. package/dist/core/agent/agents-md.js +9 -3
  258. package/dist/core/agent/agents-md.js.map +1 -1
  259. package/dist/core/agent/agents-stamp.js +10 -0
  260. package/dist/core/agent/agents-stamp.js.map +1 -1
  261. package/dist/core/agent/scaffold.js +10 -15
  262. package/dist/core/agent/scaffold.js.map +1 -1
  263. package/dist/core/agent/tools/dialects.js +1 -0
  264. package/dist/core/agent/tools/dialects.js.map +1 -1
  265. package/dist/core/agent/tools/registry.js +41 -76
  266. package/dist/core/agent/tools/registry.js.map +1 -1
  267. package/dist/core/agent/workflows/adopt.js +5 -3
  268. package/dist/core/agent/workflows/adopt.js.map +1 -1
  269. package/dist/core/agent/workflows/check.js +20 -16
  270. package/dist/core/agent/workflows/check.js.map +1 -1
  271. package/dist/core/agent/workflows/closing.js +54 -21
  272. package/dist/core/agent/workflows/closing.js.map +1 -1
  273. package/dist/core/agent/workflows/feature.js +79 -11
  274. package/dist/core/agent/workflows/feature.js.map +1 -1
  275. package/dist/core/agent/workflows/implement.js +91 -13
  276. package/dist/core/agent/workflows/implement.js.map +1 -1
  277. package/dist/core/agent/workflows/reference/authoring.js +3 -1
  278. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  279. package/dist/core/agent/workflows/reference/codes.js +8 -6
  280. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  281. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  282. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  283. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  284. package/dist/core/agent/workflows/reference/spine.js +50 -18
  285. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  286. package/dist/core/asyncapi/baseline/plan.js +14 -81
  287. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  288. package/dist/core/asyncapi/digest.js +30 -25
  289. package/dist/core/asyncapi/digest.js.map +1 -1
  290. package/dist/core/asyncapi/merge/components.js +5 -4
  291. package/dist/core/asyncapi/merge/components.js.map +1 -1
  292. package/dist/core/asyncapi/merge/error.js +7 -2
  293. package/dist/core/asyncapi/merge/error.js.map +1 -1
  294. package/dist/core/asyncapi/merge/markers.js +30 -50
  295. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  296. package/dist/core/asyncapi/merge/merge.js +31 -53
  297. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  298. package/dist/core/asyncapi/merge/pin.js +10 -36
  299. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  300. package/dist/core/asyncapi/model.js.map +1 -1
  301. package/dist/core/asyncapi/read.js +36 -12
  302. package/dist/core/asyncapi/read.js.map +1 -1
  303. package/dist/core/brief/brief.js +6 -18
  304. package/dist/core/brief/brief.js.map +1 -1
  305. package/dist/core/brief/landscape.js +8 -12
  306. package/dist/core/brief/landscape.js.map +1 -1
  307. package/dist/core/brief/map/extend.js +8 -5
  308. package/dist/core/brief/map/extend.js.map +1 -1
  309. package/dist/core/brief/map/owed.js +8 -26
  310. package/dist/core/brief/map/owed.js.map +1 -1
  311. package/dist/core/brief/targets.js +15 -5
  312. package/dist/core/brief/targets.js.map +1 -1
  313. package/dist/core/c4/likec4.js +147 -16
  314. package/dist/core/c4/likec4.js.map +1 -1
  315. package/dist/core/c4/locate/unlocated.js +150 -0
  316. package/dist/core/c4/locate/unlocated.js.map +1 -0
  317. package/dist/core/c4/parsed/values.js +54 -1
  318. package/dist/core/c4/parsed/values.js.map +1 -1
  319. package/dist/core/c4/prefetch/fleet.js +1 -9
  320. package/dist/core/c4/prefetch/fleet.js.map +1 -1
  321. package/dist/core/c4/project/documents.js +31 -6
  322. package/dist/core/c4/project/documents.js.map +1 -1
  323. package/dist/core/c4/project/load.js +6 -17
  324. package/dist/core/c4/project/load.js.map +1 -1
  325. package/dist/core/c4/resolve/external.js +51 -0
  326. package/dist/core/c4/resolve/external.js.map +1 -0
  327. package/dist/core/c4/resolve/service.js +25 -1
  328. package/dist/core/c4/resolve/service.js.map +1 -1
  329. package/dist/core/c4/seed/template.js +15 -1
  330. package/dist/core/c4/seed/template.js.map +1 -1
  331. package/dist/core/c4/service-model/shape.js +2 -22
  332. package/dist/core/c4/service-model/shape.js.map +1 -1
  333. package/dist/core/c4/source-mask.js +288 -4
  334. package/dist/core/c4/source-mask.js.map +1 -1
  335. package/dist/core/c4/source-scan.js +203 -147
  336. package/dist/core/c4/source-scan.js.map +1 -1
  337. package/dist/core/c4/splice/authored-source.js +19 -0
  338. package/dist/core/c4/splice/authored-source.js.map +1 -1
  339. package/dist/core/c4/splice/delta-blocks.js +5 -18
  340. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  341. package/dist/core/c4/splice/landscape-merge.js +13 -8
  342. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  343. package/dist/core/c4/splice/model/merge.js +10 -6
  344. package/dist/core/c4/splice/model/merge.js.map +1 -1
  345. package/dist/core/c4/splice/placement.js +108 -18
  346. package/dist/core/c4/splice/placement.js.map +1 -1
  347. package/dist/core/c4/workspace.js +13 -22
  348. package/dist/core/c4/workspace.js.map +1 -1
  349. package/dist/core/capabilities/capabilities.js +6 -6
  350. package/dist/core/capabilities/capabilities.js.map +1 -1
  351. package/dist/core/capabilities/delta/doc.js +7 -10
  352. package/dist/core/capabilities/delta/doc.js.map +1 -1
  353. package/dist/core/capabilities/findings.js +15 -20
  354. package/dist/core/capabilities/findings.js.map +1 -1
  355. package/dist/core/capabilities/realizes/findings.js +12 -19
  356. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  357. package/dist/core/capabilities/realizes/join.js +0 -20
  358. package/dist/core/capabilities/realizes/join.js.map +1 -1
  359. package/dist/core/capabilities/rollup.js +6 -0
  360. package/dist/core/capabilities/rollup.js.map +1 -1
  361. package/dist/core/capabilities/tree.js +6 -28
  362. package/dist/core/capabilities/tree.js.map +1 -1
  363. package/dist/core/capabilities/usecase-join.js +3 -13
  364. package/dist/core/capabilities/usecase-join.js.map +1 -1
  365. package/dist/core/coherence/authoring/scaffold.js +11 -3
  366. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  367. package/dist/core/coherence/authoring/sentinels.js +2 -1
  368. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  369. package/dist/core/coherence/coherence.js +30 -16
  370. package/dist/core/coherence/coherence.js.map +1 -1
  371. package/dist/core/coherence/consumed/operations.js +70 -0
  372. package/dist/core/coherence/consumed/operations.js.map +1 -0
  373. package/dist/core/coherence/consumed/refusal.js +38 -0
  374. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  375. package/dist/core/coherence/declared.js +60 -34
  376. package/dist/core/coherence/declared.js.map +1 -1
  377. package/dist/core/coherence/events/declared.js +44 -59
  378. package/dist/core/coherence/events/declared.js.map +1 -1
  379. package/dist/core/coherence/events/events.js +119 -8
  380. package/dist/core/coherence/events/events.js.map +1 -1
  381. package/dist/core/coherence/events/grades.js +31 -8
  382. package/dist/core/coherence/events/grades.js.map +1 -1
  383. package/dist/core/coherence/events/lookups.js +14 -82
  384. package/dist/core/coherence/events/lookups.js.map +1 -1
  385. package/dist/core/coherence/lookups.js +18 -78
  386. package/dist/core/coherence/lookups.js.map +1 -1
  387. package/dist/core/coherence/retired/archived.js +285 -0
  388. package/dist/core/coherence/retired/archived.js.map +1 -0
  389. package/dist/core/coherence/retired/event-pair.js +62 -0
  390. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  391. package/dist/core/coherence/retired/merged.js +79 -0
  392. package/dist/core/coherence/retired/merged.js.map +1 -0
  393. package/dist/core/coherence/retired/retirement.js +49 -0
  394. package/dist/core/coherence/retired/retirement.js.map +1 -0
  395. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  396. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  397. package/dist/core/coherence/retired/revival/revived.js +297 -0
  398. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  399. package/dist/core/consumers/after.js +113 -0
  400. package/dist/core/consumers/after.js.map +1 -0
  401. package/dist/core/consumers/consumers.js +296 -0
  402. package/dist/core/consumers/consumers.js.map +1 -0
  403. package/dist/core/consumers/current.js +211 -0
  404. package/dist/core/consumers/current.js.map +1 -0
  405. package/dist/core/consumers/scan.js +46 -0
  406. package/dist/core/consumers/scan.js.map +1 -0
  407. package/dist/core/consumers/state.js +27 -0
  408. package/dist/core/consumers/state.js.map +1 -0
  409. package/dist/core/delta/delta.js +1 -1
  410. package/dist/core/delta/delta.js.map +1 -1
  411. package/dist/core/deployment/delta.js +3 -6
  412. package/dist/core/deployment/delta.js.map +1 -1
  413. package/dist/core/diff/base-git.js +94 -12
  414. package/dist/core/diff/base-git.js.map +1 -1
  415. package/dist/core/diff/base-state.js +28 -16
  416. package/dist/core/diff/base-state.js.map +1 -1
  417. package/dist/core/diff/joins.js +136 -0
  418. package/dist/core/diff/joins.js.map +1 -0
  419. package/dist/core/diff/scope/changed-paths.js +22 -6
  420. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  421. package/dist/core/diff/scope/edges.js +82 -0
  422. package/dist/core/diff/scope/edges.js.map +1 -0
  423. package/dist/core/diff/semantic.js +100 -31
  424. package/dist/core/diff/semantic.js.map +1 -1
  425. package/dist/core/doctor/agents.js +34 -18
  426. package/dist/core/doctor/agents.js.map +1 -1
  427. package/dist/core/doctor/report.js.map +1 -1
  428. package/dist/core/document/apply.js +10 -2
  429. package/dist/core/document/apply.js.map +1 -1
  430. package/dist/core/document/frontmatter.js +55 -0
  431. package/dist/core/document/frontmatter.js.map +1 -1
  432. package/dist/core/document/parse.js +29 -6
  433. package/dist/core/document/parse.js.map +1 -1
  434. package/dist/core/document/scenarios.js +17 -0
  435. package/dist/core/document/scenarios.js.map +1 -1
  436. package/dist/core/document/spec.js +4 -11
  437. package/dist/core/document/spec.js.map +1 -1
  438. package/dist/core/envelope/config.js +10 -0
  439. package/dist/core/envelope/config.js.map +1 -1
  440. package/dist/core/envelope/json.js +34 -0
  441. package/dist/core/envelope/json.js.map +1 -1
  442. package/dist/core/explain/families.js +17 -12
  443. package/dist/core/explain/families.js.map +1 -1
  444. package/dist/core/explain/refusals.js +3 -3
  445. package/dist/core/explain/refusals.js.map +1 -1
  446. package/dist/core/explain/terms.js +18 -9
  447. package/dist/core/explain/terms.js.map +1 -1
  448. package/dist/core/explore/explore.js +2 -7
  449. package/dist/core/explore/explore.js.map +1 -1
  450. package/dist/core/fleet-context.js +34 -95
  451. package/dist/core/fleet-context.js.map +1 -1
  452. package/dist/core/gate/checks.js +5 -2
  453. package/dist/core/gate/checks.js.map +1 -1
  454. package/dist/core/gate/partners.js +4 -2
  455. package/dist/core/gate/partners.js.map +1 -1
  456. package/dist/core/gate/report.js.map +1 -1
  457. package/dist/core/gate/verification.js +75 -12
  458. package/dist/core/gate/verification.js.map +1 -1
  459. package/dist/core/gherkin/stale.js +13 -37
  460. package/dist/core/gherkin/stale.js.map +1 -1
  461. package/dist/core/glossary/tree.js +6 -19
  462. package/dist/core/glossary/tree.js.map +1 -1
  463. package/dist/core/kernel/concurrency.js +19 -0
  464. package/dist/core/kernel/concurrency.js.map +1 -1
  465. package/dist/core/kernel/records.js +11 -0
  466. package/dist/core/kernel/records.js.map +1 -1
  467. package/dist/core/links/backlinks.js +1 -3
  468. package/dist/core/links/backlinks.js.map +1 -1
  469. package/dist/core/links/corpus.js +5 -36
  470. package/dist/core/links/corpus.js.map +1 -1
  471. package/dist/core/mcp/argv.js +101 -0
  472. package/dist/core/mcp/argv.js.map +1 -1
  473. package/dist/core/mcp/author-tools.js +2 -1
  474. package/dist/core/mcp/author-tools.js.map +1 -1
  475. package/dist/core/mcp/protocol.js +2 -2
  476. package/dist/core/mcp/protocol.js.map +1 -1
  477. package/dist/core/mcp/tools.js +43 -33
  478. package/dist/core/mcp/tools.js.map +1 -1
  479. package/dist/core/openapi/baseline/gate.js +53 -12
  480. package/dist/core/openapi/baseline/gate.js.map +1 -1
  481. package/dist/core/openapi/baseline/plan.js +22 -8
  482. package/dist/core/openapi/baseline/plan.js.map +1 -1
  483. package/dist/core/openapi/baseline/record.js +0 -1
  484. package/dist/core/openapi/baseline/record.js.map +1 -1
  485. package/dist/core/openapi/contract-text.js +33 -0
  486. package/dist/core/openapi/contract-text.js.map +1 -0
  487. package/dist/core/openapi/contract-tree.js +56 -0
  488. package/dist/core/openapi/contract-tree.js.map +1 -0
  489. package/dist/core/openapi/depth.js +38 -24
  490. package/dist/core/openapi/depth.js.map +1 -1
  491. package/dist/core/openapi/digest.js +52 -2
  492. package/dist/core/openapi/digest.js.map +1 -1
  493. package/dist/core/openapi/doc.js +12 -4
  494. package/dist/core/openapi/doc.js.map +1 -1
  495. package/dist/core/openapi/merge/components.js +25 -60
  496. package/dist/core/openapi/merge/components.js.map +1 -1
  497. package/dist/core/openapi/merge/error.js +3 -13
  498. package/dist/core/openapi/merge/error.js.map +1 -1
  499. package/dist/core/openapi/merge/markers.js +26 -30
  500. package/dist/core/openapi/merge/markers.js.map +1 -1
  501. package/dist/core/openapi/merge/merge.js +26 -52
  502. package/dist/core/openapi/merge/merge.js.map +1 -1
  503. package/dist/core/openapi/merge/pin.js +15 -35
  504. package/dist/core/openapi/merge/pin.js.map +1 -1
  505. package/dist/core/openspec/decide/verdict.js.map +1 -1
  506. package/dist/core/openspec/inventory.js +4 -5
  507. package/dist/core/openspec/inventory.js.map +1 -1
  508. package/dist/core/openspec/model/mapping.js +30 -28
  509. package/dist/core/openspec/model/mapping.js.map +1 -1
  510. package/dist/core/openspec/model/model.js +12 -7
  511. package/dist/core/openspec/model/model.js.map +1 -1
  512. package/dist/core/openspec/scan/changes.js +10 -4
  513. package/dist/core/openspec/scan/changes.js.map +1 -1
  514. package/dist/core/openspec/scan/config.js +7 -16
  515. package/dist/core/openspec/scan/config.js.map +1 -1
  516. package/dist/core/openspec/scan/shape.js +63 -19
  517. package/dist/core/openspec/scan/shape.js.map +1 -1
  518. package/dist/core/openspec/scan/walk.js +21 -38
  519. package/dist/core/openspec/scan/walk.js.map +1 -1
  520. package/dist/core/pack/living.js +2 -16
  521. package/dist/core/pack/living.js.map +1 -1
  522. package/dist/core/provenance/findings.js +48 -12
  523. package/dist/core/provenance/findings.js.map +1 -1
  524. package/dist/core/provenance/git.js +33 -57
  525. package/dist/core/provenance/git.js.map +1 -1
  526. package/dist/core/provenance/gitq/moves.js +71 -114
  527. package/dist/core/provenance/gitq/moves.js.map +1 -1
  528. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  529. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  530. package/dist/core/provenance/sources.js +23 -6
  531. package/dist/core/provenance/sources.js.map +1 -1
  532. package/dist/core/provenance/stamp.js +5 -17
  533. package/dist/core/provenance/stamp.js.map +1 -1
  534. package/dist/core/repo/entries.js.map +1 -1
  535. package/dist/core/repo/repo.js +24 -14
  536. package/dist/core/repo/repo.js.map +1 -1
  537. package/dist/core/repo/tree/find.js +12 -3
  538. package/dist/core/repo/tree/find.js.map +1 -1
  539. package/dist/core/repo/tree/fs.js +27 -1
  540. package/dist/core/repo/tree/fs.js.map +1 -1
  541. package/dist/core/repo/tree/walk.js +8 -30
  542. package/dist/core/repo/tree/walk.js.map +1 -1
  543. package/dist/core/results.js +47 -12
  544. package/dist/core/results.js.map +1 -1
  545. package/dist/core/staging/commit.js +10 -12
  546. package/dist/core/staging/commit.js.map +1 -1
  547. package/dist/core/staging/lock.js +74 -12
  548. package/dist/core/staging/lock.js.map +1 -1
  549. package/dist/core/staging/recovery/intent.js +2 -5
  550. package/dist/core/staging/recovery/intent.js.map +1 -1
  551. package/dist/core/staging/recovery/recover.js +60 -1
  552. package/dist/core/staging/recovery/recover.js.map +1 -1
  553. package/dist/core/staging/snapshot/manifest.js +152 -0
  554. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  555. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
  556. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  557. package/dist/core/status/actions/execution.js +11 -0
  558. package/dist/core/status/actions/execution.js.map +1 -1
  559. package/dist/core/status/feature/artifacts.js +2 -2
  560. package/dist/core/status/feature/artifacts.js.map +1 -1
  561. package/dist/core/status/feature/feature.js +5 -11
  562. package/dist/core/status/feature/feature.js.map +1 -1
  563. package/dist/core/status/fleet/fleet.js +6 -2
  564. package/dist/core/status/fleet/fleet.js.map +1 -1
  565. package/dist/core/status/fleet/next.js +26 -3
  566. package/dist/core/status/fleet/next.js.map +1 -1
  567. package/dist/core/status/report.js.map +1 -1
  568. package/dist/core/status/verification.js +30 -15
  569. package/dist/core/status/verification.js.map +1 -1
  570. package/dist/core/tasks/packet.js +174 -0
  571. package/dist/core/tasks/packet.js.map +1 -0
  572. package/dist/core/tasks/record.js +43 -0
  573. package/dist/core/tasks/record.js.map +1 -0
  574. package/dist/core/tasks/sources.js +181 -0
  575. package/dist/core/tasks/sources.js.map +1 -0
  576. package/dist/core/tasks/stamps.js +76 -0
  577. package/dist/core/tasks/stamps.js.map +1 -0
  578. package/dist/core/tasks/unit.js +0 -0
  579. package/dist/core/tasks/unit.js.map +1 -0
  580. package/dist/core/usecases/capability.js +11 -14
  581. package/dist/core/usecases/capability.js.map +1 -1
  582. package/dist/core/usecases/delta/flows.js +5 -7
  583. package/dist/core/usecases/delta/flows.js.map +1 -1
  584. package/dist/core/usecases/fleet.js +178 -31
  585. package/dist/core/usecases/fleet.js.map +1 -1
  586. package/dist/core/usecases/operations.js +5 -4
  587. package/dist/core/usecases/operations.js.map +1 -1
  588. package/dist/core/usecases/service/flows.js +9 -6
  589. package/dist/core/usecases/service/flows.js.map +1 -1
  590. package/dist/core/usecases/suites/participants.js +83 -0
  591. package/dist/core/usecases/suites/participants.js.map +1 -0
  592. package/dist/core/usecases/suites/suites.js +45 -0
  593. package/dist/core/usecases/suites/suites.js.map +1 -0
  594. package/dist/core/verify/answers.js +12 -5
  595. package/dist/core/verify/answers.js.map +1 -1
  596. package/dist/core/verify/build.js +102 -82
  597. package/dist/core/verify/build.js.map +1 -1
  598. package/dist/core/verify/checklist.js +3 -0
  599. package/dist/core/verify/checklist.js.map +1 -1
  600. package/dist/core/verify/location.js +67 -0
  601. package/dist/core/verify/location.js.map +1 -0
  602. package/dist/core/verify/pins/lint.js +3 -2
  603. package/dist/core/verify/pins/lint.js.map +1 -1
  604. package/dist/core/verify/pins/pin.js +1 -1
  605. package/dist/core/verify/pins/pin.js.map +1 -1
  606. package/dist/core/verify/read/file.js +224 -0
  607. package/dist/core/verify/read/file.js.map +1 -0
  608. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  609. package/dist/core/verify/read/shape.js.map +1 -0
  610. package/dist/core/verify/read/union.js +105 -0
  611. package/dist/core/verify/read/union.js.map +1 -0
  612. package/dist/core/verify/record.js +35 -12
  613. package/dist/core/verify/record.js.map +1 -1
  614. package/dist/core/verify/store/commit.js +40 -30
  615. package/dist/core/verify/store/commit.js.map +1 -1
  616. package/dist/core/verify/store/render.js +8 -4
  617. package/dist/core/verify/store/render.js.map +1 -1
  618. package/dist/core/vocabulary/issue.js +1 -0
  619. package/dist/core/vocabulary/issue.js.map +1 -1
  620. package/dist/core/vocabulary/maturity.js +12 -2
  621. package/dist/core/vocabulary/maturity.js.map +1 -1
  622. package/examples/README.md +21 -6
  623. package/examples/docs/architecture/landscape.likec4 +8 -0
  624. package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
  625. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  626. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  627. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  628. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  629. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  630. package/package.json +1 -1
  631. package/dist/commands/validate/checks/vocabulary.js +0 -16
  632. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  633. package/dist/core/agent/agents-md/artifacts.js +0 -221
  634. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  635. package/dist/core/agent/agents-md/cycle.js +0 -180
  636. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  637. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  638. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  639. package/dist/core/agent/agents-md/spine.js +0 -40
  640. package/dist/core/agent/agents-md/spine.js.map +0 -1
  641. package/dist/core/coherence/events/merged.js +0 -40
  642. package/dist/core/coherence/events/merged.js.map +0 -1
  643. package/dist/core/diff/current-state.js +0 -96
  644. package/dist/core/diff/current-state.js.map +0 -1
  645. package/dist/core/diff/victims.js +0 -256
  646. package/dist/core/diff/victims.js.map +0 -1
  647. package/dist/core/staging/snapshot.js.map +0 -1
  648. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../../src/core/c4/service-model/shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI3D;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,6BAA6B,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAClC,2EAA2E;IAC3E,0EAA0E;IAC1E,8EAA8E;IAC9E,mBAAmB;IACnB,MAAM,aAAa,GAAG,uBAAuB,CAAC;IAC9C,KAAK,IAAI,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/C,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,SAAS;QACnD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/E,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,YAAY,CAAC;IACnD,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,GAAe,EAAE,CAAC,YAAY,CAAC,CAAC;AACjF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAwB;IAC5D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,cAAc,CAAC,IAAI,CAAC,CAAU,CAAC,CAAC;IAC3G,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAC;aAC3B,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * Which of the two shapes a `model.likec4` has, decided by a byte scan of the\n * file itself.\n *\n * A STANDALONE model declares at least one `element <kind>` inside a top-level\n * `specification { }` block. It is parsed ALONE, exactly as every model was\n * before this axis: it may re-declare the partners it talks to, and it renders\n * only as a project of its own. An EXTENDING model declares no element kind —\n * it takes the map's — and says what is inside the service with\n * `extend <fqn> { … }`; it is parsed INSIDE the fleet's `architecture/`\n * project, beside the map that declares those kinds.\n *\n * THE FILE'S OWN GRAMMAR DECIDES, never a config key, and that is the whole\n * design of this module. A flag in `loam.json` would be a second authority on a\n * fact the document already states, free to disagree with it — and the\n * disagreement is not a warning but a parse: a model read the wrong way is\n * either eleven `c4.invalid` errors (an extending model parsed alone: measured,\n * deleting a model's `specification` under the old loader) or a pile of\n * duplicate-declaration errors blamed on BOTH files (a standalone model staged\n * beside the landscape). Nothing about a model has to be migrated for loam to\n * read it correctly; the bytes say which it is.\n *\n * Every other answer is `extending`, and each of those is legal on purpose: a\n * tags-only `specification { tag req-X }` (measured legal inside one project,\n * and it lands the tag in the project's specification), a bare `model {}`, an\n * empty file, a file that is only `views { }`. The conservative direction is\n * therefore towards `extending` — which is why an unreadable file is NOT\n * classified here at all (see `FleetContext.serviceModel`): absence is a\n * question for the reader that establishes the file exists, not for a scanner\n * looking at bytes it does not have.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { inOrder } from \"../../kernel/concurrency.js\";\nimport { maskSource, matchBrace } from \"../source-mask.js\";\n\nexport type ModelShape = \"standalone\" | \"extending\";\n\n/**\n * A kind declaration inside a specification block — `element service`,\n * `element softwareSystem`. Deliberately only `element`: `tag`,\n * `relationship`, `deploymentNode` and `color` declare no ELEMENT kind, and a\n * model whose specification holds only those still takes every element kind it\n * uses from the map, which is exactly what makes it extending.\n */\nconst ELEMENT_KIND = /\\belement\\s+[A-Za-z_][\\w-]*/;\n\n/**\n * The shape of a model, from its source text.\n *\n * The scan runs over MASKED source (`../source-mask.ts`), so a comment or a\n * string containing `specification { element x }` counts for nothing — and that\n * is not a hypothetical: the brief loam writes for an adopted service explains\n * the other shape in a comment, so an authored model can easily carry those\n * exact words in prose. Reading them as a declaration would flip a whole\n * service to the wrong loader.\n *\n * Only TOP-LEVEL blocks are considered — depth zero in the masked source — for\n * the same reason: `specification` nested inside another block is not a\n * declaration LikeC4 would honour, so loam must not honour it either.\n *\n * An unbalanced block (no closing brace) is read to end of file and still\n * answers `standalone` when it declares a kind. That file does not parse at\n * all, and both loaders will say so; answering `standalone` sends the reader to\n * the one whose errors name only this file, which is the honest half of a\n * broken document.\n */\nexport function modelShape(text: string): ModelShape {\n const { code } = maskSource(text);\n // Built per call rather than hoisted to module scope: a `/g` regex carries\n // `lastIndex`, which is mutable state a second caller would inherit — the\n // module-level-state hazard AGENTS.md names, arriving through a constant that\n // looks immutable.\n const specification = /\\bspecification\\s*\\{/g;\n for (let match = specification.exec(code); match !== null; match = specification.exec(code)) {\n const open = match.index + match[0].length - 1;\n if (depthBefore(code, match.index) !== 0) continue;\n const close = matchBrace(code, open);\n const body = close === -1 ? code.slice(open + 1) : code.slice(open + 1, close);\n if (ELEMENT_KIND.test(body)) return \"standalone\";\n }\n return \"extending\";\n}\n\n/**\n * One model's shape, from its bytes — the shape of a file that cannot be read\n * is `standalone`.\n *\n * That is the conservative arm rather than a guess. The standalone path hands\n * the model to the per-file loader, so a caller asking about a file that is not\n * there (or is not readable) gets today's exact error — `c4.invalid`, naming\n * that file — instead of a slice of the fleet map, which is what the extending\n * path would answer about a document nobody could open.\n */\nexport async function readModelShape(path: string): Promise<ModelShape> {\n return readFile(path, \"utf8\").then(modelShape, (): ModelShape => \"standalone\");\n}\n\n/**\n * Many models' shapes at once, keyed by RESOLVED path.\n *\n * The fleet needs this list before it can do anything else: which models are\n * batched as projects and which as documents, which directories are owed a\n * per-service `likec4.config.json` and which must not have one. Every one of\n * those questions is asked over the FULL service enumeration rather than over\n * whatever subset a run is grading, so the read is a bulk one through the\n * shared pool — a fleet of 56 models is 56 small reads, and the pool is what\n * keeps them from being 56 sequential round-trips on a network-mounted docs\n * repo.\n */\nexport async function readModelShapes(paths: readonly string[]): Promise<Map<string, ModelShape>> {\n const entries = await inOrder(paths, async (path) => [resolve(path), await readModelShape(path)] as const);\n return new Map(entries);\n}\n\n/**\n * How many blocks are open at `index` in masked code. A plain count, because\n * the mask has already blanked every brace inside a string or a comment — the\n * one thing that would make counting lie.\n */\nfunction depthBefore(code: string, index: number): number {\n let depth = 0;\n for (let i = 0; i < index; i += 1) {\n if (code[i] === \"{\") depth += 1;\n else if (code[i] === \"}\") depth -= 1;\n }\n return depth;\n}\n"]}
1
+ {"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../../src/core/c4/service-model/shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAI/D;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,6BAA6B,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAClC,2EAA2E;IAC3E,0EAA0E;IAC1E,8EAA8E;IAC9E,mBAAmB;IACnB,MAAM,aAAa,GAAG,uBAAuB,CAAC;IAC9C,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC;QAC3D,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,YAAY,CAAC;IACnD,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY;IAC/C,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,GAAe,EAAE,CAAC,YAAY,CAAC,CAAC;AACjF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAwB;IAC5D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,cAAc,CAAC,IAAI,CAAC,CAAU,CAAC,CAAC;IAC3G,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * Which of the two shapes a `model.likec4` has, decided by a byte scan of the\n * file itself.\n *\n * A STANDALONE model declares at least one `element <kind>` inside a top-level\n * `specification { }` block. It is parsed ALONE, exactly as every model was\n * before this axis: it may re-declare the partners it talks to, and it renders\n * only as a project of its own. An EXTENDING model declares no element kind —\n * it takes the map's — and says what is inside the service with\n * `extend <fqn> { … }`; it is parsed INSIDE the fleet's `architecture/`\n * project, beside the map that declares those kinds.\n *\n * THE FILE'S OWN GRAMMAR DECIDES, never a config key, and that is the whole\n * design of this module. A flag in `loam.json` would be a second authority on a\n * fact the document already states, free to disagree with it — and the\n * disagreement is not a warning but a parse: a model read the wrong way is\n * either eleven `c4.invalid` errors (an extending model parsed alone: measured,\n * deleting a model's `specification` under the old loader) or a pile of\n * duplicate-declaration errors blamed on BOTH files (a standalone model staged\n * beside the landscape). Nothing about a model has to be migrated for loam to\n * read it correctly; the bytes say which it is.\n *\n * Every other answer is `extending`, and each of those is legal on purpose: a\n * tags-only `specification { tag req-X }` (measured legal inside one project,\n * and it lands the tag in the project's specification), a bare `model {}`, an\n * empty file, a file that is only `views { }`. The conservative direction is\n * therefore towards `extending` — which is why an unreadable file is NOT\n * classified here at all (see `FleetContext.serviceModel`): absence is a\n * question for the reader that establishes the file exists, not for a scanner\n * looking at bytes it does not have.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { inOrder } from \"../../kernel/concurrency.js\";\nimport { maskSource, topLevelBlocks } from \"../source-mask.js\";\n\nexport type ModelShape = \"standalone\" | \"extending\";\n\n/**\n * A kind declaration inside a specification block — `element service`,\n * `element softwareSystem`. Deliberately only `element`: `tag`,\n * `relationship`, `deploymentNode` and `color` declare no ELEMENT kind, and a\n * model whose specification holds only those still takes every element kind it\n * uses from the map, which is exactly what makes it extending.\n */\nconst ELEMENT_KIND = /\\belement\\s+[A-Za-z_][\\w-]*/;\n\n/**\n * The shape of a model, from its source text.\n *\n * The scan runs over MASKED source (`../source-mask.ts`), so a comment or a\n * string containing `specification { element x }` counts for nothing — and that\n * is not a hypothetical: the brief loam writes for an adopted service explains\n * the other shape in a comment, so an authored model can easily carry those\n * exact words in prose. Reading them as a declaration would flip a whole\n * service to the wrong loader.\n *\n * Only TOP-LEVEL blocks are considered — depth zero in the masked source — for\n * the same reason: `specification` nested inside another block is not a\n * declaration LikeC4 would honour, so loam must not honour it either.\n *\n * An unbalanced block (no closing brace) is read to end of file and still\n * answers `standalone` when it declares a kind. That file does not parse at\n * all, and both loaders will say so; answering `standalone` sends the reader to\n * the one whose errors name only this file, which is the honest half of a\n * broken document.\n */\nexport function modelShape(text: string): ModelShape {\n const { code } = maskSource(text);\n // Built per call rather than hoisted to module scope: a `/g` regex carries\n // `lastIndex`, which is mutable state a second caller would inherit — the\n // module-level-state hazard AGENTS.md names, arriving through a constant that\n // looks immutable.\n const specification = /\\bspecification\\s*\\{/g;\n for (const { body } of topLevelBlocks(code, specification)) {\n if (ELEMENT_KIND.test(body)) return \"standalone\";\n }\n return \"extending\";\n}\n\n/**\n * One model's shape, from its bytes — the shape of a file that cannot be read\n * is `standalone`.\n *\n * That is the conservative arm rather than a guess. The standalone path hands\n * the model to the per-file loader, so a caller asking about a file that is not\n * there (or is not readable) gets today's exact error — `c4.invalid`, naming\n * that file — instead of a slice of the fleet map, which is what the extending\n * path would answer about a document nobody could open.\n */\nexport async function readModelShape(path: string): Promise<ModelShape> {\n return readFile(path, \"utf8\").then(modelShape, (): ModelShape => \"standalone\");\n}\n\n/**\n * Many models' shapes at once, keyed by RESOLVED path.\n *\n * The fleet needs this list before it can do anything else: which models are\n * batched as projects and which as documents, which directories are owed a\n * per-service `likec4.config.json` and which must not have one. Every one of\n * those questions is asked over the FULL service enumeration rather than over\n * whatever subset a run is grading, so the read is a bulk one through the\n * shared pool — a fleet of 56 models is 56 small reads, and the pool is what\n * keeps them from being 56 sequential round-trips on a network-mounted docs\n * repo.\n */\nexport async function readModelShapes(paths: readonly string[]): Promise<Map<string, ModelShape>> {\n const entries = await inOrder(paths, async (path) => [resolve(path), await readModelShape(path)] as const);\n return new Map(entries);\n}\n"]}
@@ -1,15 +1,84 @@
1
+ /**
2
+ * LikeC4's MARKDOWN string: `'''…'''` or `"""…"""`, closed by the first
3
+ * matching triple and read with no escapes (the grammar's `MarkdownString` is
4
+ * an until-token, not a quoted run). It is what a long `description` is
5
+ * written in, and the reason it is its own state rather than three ordinary
6
+ * quotes: read as `''` + `'…'` + `''`, the first apostrophe or odd quote in the
7
+ * prose — "it's" — ended the literal, every brace after it was CODE, and the
8
+ * rest of the document was read inside out. A map with one such description
9
+ * above its model had no model block at all as far as the splice map could
10
+ * tell, and archive refused a landscape LikeC4 parses cleanly.
11
+ */
12
+ const MARKDOWN_QUOTES = ["'''", '"""'];
13
+ /**
14
+ * Where a URI token starting at `at` ends, or -1 when none starts there.
15
+ *
16
+ * LikeC4 lexes `scheme://…` and a relative `./…`, `../…` or `/…` path as ONE
17
+ * token running to the next whitespace — tried before the comment tokens can
18
+ * see the `//` inside it, because the token starts at the scheme, not at the
19
+ * slash. Read as code, `link https://…` opened a line comment at the `//`, and
20
+ * that blanked whatever followed on the line: `x = kind 'X' { link
21
+ * https://… }` lost its closing brace, and the scan found no model block in a
22
+ * map LikeC4 parses. A URI's interior is never structure — a `{id}`, a `#anchor`,
23
+ * an apostrophe are all part of the token — so it is blanked like a literal's.
24
+ * It is recorded in `uris`, not among the literals, because it is no string —
25
+ * no title is read from it — but it IS a token: blanked and recorded nowhere,
26
+ * `link https://…` read as the bare word `link`, and `statementHead` ran that
27
+ * on into the next line and swallowed the declaration written there.
28
+ */
29
+ function uriEnd(text, at) {
30
+ if (at > 0 && /[\w.-]/.test(text[at - 1]))
31
+ return -1;
32
+ let j = at;
33
+ if (/\w/.test(text[at])) {
34
+ while (j < text.length && /\w/.test(text[j]))
35
+ j += 1;
36
+ if (!text.startsWith("://", j))
37
+ return -1;
38
+ j += 3;
39
+ }
40
+ else {
41
+ while (j < at + 2 && text[j] === ".")
42
+ j += 1;
43
+ const after = text[j + 1];
44
+ // `//` and `/*` with no dot before them are the comment tokens, which win there.
45
+ if (text[j] !== "/" || after === undefined || after === "/" || /\s/.test(after) || (j === at && after === "*"))
46
+ return -1;
47
+ }
48
+ while (j < text.length && !/\s/.test(text[j]))
49
+ j += 1;
50
+ return j;
51
+ }
1
52
  export function maskSource(text) {
2
53
  const out = text.split("");
3
54
  const literals = [];
55
+ const uris = [];
4
56
  let state = "code";
5
57
  let litStart = -1;
6
58
  let lit = "";
59
+ let fence = "";
7
60
  for (let i = 0; i < text.length; i += 1) {
8
61
  const ch = text[i];
9
62
  const next = text[i + 1];
10
63
  switch (state) {
11
- case "code":
12
- if (ch === "'" || ch === '"') {
64
+ case "code": {
65
+ const uri = uriEnd(text, i);
66
+ if (uri !== -1) {
67
+ uris.push({ start: i, end: uri });
68
+ for (; i < uri; i += 1)
69
+ out[i] = " ";
70
+ i -= 1;
71
+ break;
72
+ }
73
+ fence = MARKDOWN_QUOTES.find((q) => text.startsWith(q, i)) ?? "";
74
+ if (fence !== "") {
75
+ state = "markdown";
76
+ litStart = i;
77
+ lit = "";
78
+ out[i] = out[i + 1] = out[i + 2] = " ";
79
+ i += 2;
80
+ }
81
+ else if (ch === "'" || ch === '"') {
13
82
  state = ch === "'" ? "squote" : "dquote";
14
83
  litStart = i;
15
84
  lit = "";
@@ -24,6 +93,7 @@ export function maskSource(text) {
24
93
  out[i] = " ";
25
94
  }
26
95
  break;
96
+ }
27
97
  case "squote":
28
98
  case "dquote":
29
99
  if (ch === "\\" && i + 1 < text.length) {
@@ -43,8 +113,25 @@ export function maskSource(text) {
43
113
  out[i] = " ";
44
114
  }
45
115
  break;
116
+ case "markdown":
117
+ if (text.startsWith(fence, i)) {
118
+ literals.push({ start: litStart, end: i + 3, value: lit });
119
+ state = "code";
120
+ out[i] = out[i + 1] = out[i + 2] = " ";
121
+ i += 2;
122
+ }
123
+ else {
124
+ lit += ch;
125
+ if (ch !== "\n")
126
+ out[i] = " ";
127
+ }
128
+ break;
46
129
  case "lineComment":
47
- if (ch === "\n")
130
+ // LikeC4's line comment is `//[^\n\r]*`: a bare `\r` ends it. Masked to
131
+ // the `\n`, `a -> b // why` with a bare `\r` before the next edge hid
132
+ // that edge — `validate --all --base` never graded its op, and archive
133
+ // refused the map on a relationship count.
134
+ if (ch === "\n" || ch === "\r")
48
135
  state = "code";
49
136
  else
50
137
  out[i] = " ";
@@ -62,7 +149,7 @@ export function maskSource(text) {
62
149
  break;
63
150
  }
64
151
  }
65
- return { code: out.join(""), literals };
152
+ return { code: out.join(""), literals, uris };
66
153
  }
67
154
  /** Index of the `}` matching the `{` at `open`, over MASKED code; -1 when unbalanced. */
68
155
  export function matchBrace(code, open) {
@@ -78,5 +165,202 @@ export function matchBrace(code, open) {
78
165
  }
79
166
  return -1;
80
167
  }
168
+ /**
169
+ * Every TOP-LEVEL block in MASKED code whose opening `re` matches — `re` is
170
+ * global and its match ends at the block's `{` — with the block's body, read
171
+ * to end of file when the brace never closes. Only depth zero counts: a block
172
+ * nested inside another is not a declaration LikeC4 would honour. Depth is a
173
+ * plain count, because the mask has already blanked every brace inside a
174
+ * string or a comment — the one thing that would make counting lie — and it
175
+ * is tracked in one forward pass rather than recounted per match.
176
+ */
177
+ export function* topLevelBlocks(code, re) {
178
+ let depth = 0;
179
+ let cursor = 0;
180
+ for (const match of code.matchAll(re)) {
181
+ for (; cursor < match.index; cursor += 1) {
182
+ if (code[cursor] === "{")
183
+ depth += 1;
184
+ else if (code[cursor] === "}")
185
+ depth -= 1;
186
+ }
187
+ if (depth !== 0)
188
+ continue;
189
+ const open = match.index + match[0].length - 1;
190
+ const close = matchBrace(code, open);
191
+ yield { match, body: code.slice(open + 1, close === -1 ? code.length : close) };
192
+ }
193
+ }
194
+ /**
195
+ * The head of the statement starting at `from`, inside a block that closes at
196
+ * `close`: up to its own `{`, the enclosing block's `}`, or the end of its
197
+ * line — unless LikeC4 reads on.
198
+ *
199
+ * It does read on, because its newlines are HIDDEN tokens (`NL` in the
200
+ * grammar): a declaration may be split across lines anywhere a space could
201
+ * go. A line-bounded head made each split form either invisible — so archive
202
+ * refused a valid map — or, worse, MISREAD: `name = kind 'title'` with its `{`
203
+ * on the next line read as a bodyless element, and a neighbour spliced in after
204
+ * that line took the body for its own, a re-parse that passes over a merge
205
+ * that moved another element's description and children (probed 2026-10-20).
206
+ * The next line continues the statement when its first token can only belong
207
+ * to it:
208
+ *
209
+ * - a `{` — a body opens nothing else;
210
+ * - a string — no LikeC4 statement starts with one, so it is this one's
211
+ * title, description or technology;
212
+ * - a `#tag` — a relationship's tags may follow it on the next line, and an
213
+ * element's tags live inside its body, never after its head;
214
+ * - anything at all, when the head so far cannot end a statement: it ends in
215
+ * `=` or `->`, or it is one bare name (`entityService` above `= kind`, a
216
+ * kind above the name it declares, `link` above its URI).
217
+ *
218
+ * A line starting `->` after a COMPLETE head is a statement of its own — the
219
+ * sourceless edge from the enclosing element — which is why an arrow at the
220
+ * start of a line continues only a bare name.
221
+ *
222
+ * "Bare" is read off the tokens, not the blanks: `link https://…` and
223
+ * `description 'x'` are masked to one word each, and both are complete
224
+ * properties. Read as bare names they ran on into the next line, and whatever
225
+ * was declared there — a child, the parent's sourceless edge — was read into
226
+ * the property and lost (2e490dd blanked the URI and recorded nothing).
227
+ *
228
+ * A line ends at a carriage return as well as a line feed: LikeC4's `NL` is
229
+ * `[\r\n]+`, so a bare `\r` is a line break to it. Ended at `\n` alone, the
230
+ * head of `link https://…`, `description 'x'` or `technology 'TS'` ran on over
231
+ * a bare `\r` into the parent's sourceless edge, which was read as an edge
232
+ * FROM `link` (`description`, `technology`): the counts still agreed, nothing
233
+ * refused, and `validate --all --base` graded the branch's misspelt op nowhere
234
+ * (probed on main at 171555d). A CRLF line ends at its `\r` now rather than
235
+ * its `\n`, which nothing reads differently — `nextToken` steps over both.
236
+ */
237
+ export function statementHead(masked, from, close) {
238
+ const { code } = masked;
239
+ let j = from;
240
+ for (;;) {
241
+ while (j < close && code[j] !== "\n" && code[j] !== "\r" && code[j] !== "{" && code[j] !== "}")
242
+ j += 1;
243
+ if (j >= close)
244
+ return { end: close, opener: -1 };
245
+ if (code[j] === "{")
246
+ return { end: j, opener: j };
247
+ if (code[j] === "}")
248
+ return { end: j, opener: -1 };
249
+ const next = nextToken(masked, j, close);
250
+ if (next === -1 || !readsOn(masked, { from, end: j }, next))
251
+ return { end: j, opener: -1 };
252
+ j = next;
253
+ }
254
+ }
255
+ /**
256
+ * The first token after `at`: a literal's or a URI's start, or a non-blank
257
+ * character of code; -1 at the block's close. A URI is masked blank, so
258
+ * without its span `link` above its URI looked past the URI to the line after
259
+ * — and ran on into it.
260
+ */
261
+ function nextToken(masked, at, close) {
262
+ for (let k = at; k < close; k += 1) {
263
+ if (spanAt(masked.literals, k) || spanAt(masked.uris, k) || !/\s/.test(masked.code[k]))
264
+ return k;
265
+ }
266
+ return -1;
267
+ }
268
+ /** Whether the token at `next` belongs to the statement whose head so far spans `[from, end)`. */
269
+ function readsOn(masked, head, next) {
270
+ const { code, literals, uris } = masked;
271
+ if (spanAt(literals, next))
272
+ return true;
273
+ if (code[next] === "{" || code[next] === "#")
274
+ return true;
275
+ if (code[next] === "}")
276
+ return false;
277
+ const sofar = code.slice(head.from, head.end).trim();
278
+ if (/(?:=|->)$/.test(sofar))
279
+ return true;
280
+ // Masked, `description 'x'` reads as the one word `description` and
281
+ // `link https://…` as the one word `link`: a head holding a literal or a URI
282
+ // is never a bare name.
283
+ return !spanIn(literals, head) && !spanIn(uris, head) && /^[A-Za-z_][\w.-]*$/.test(sofar);
284
+ }
285
+ /**
286
+ * Source text from a point in CODE, past its leading blanks and every block
287
+ * comment that closes within it: what is left starts with code, with a line
288
+ * comment, with a block comment the text does not close — or is empty. Read
289
+ * off the raw text, because the mask blanks a comment exactly like a string's
290
+ * interior, and the splice must tell the two apart: a comment trailing a
291
+ * statement is room to splice past, a string is not.
292
+ */
293
+ export function pastComments(text) {
294
+ let rest = text.trimStart();
295
+ while (rest.startsWith("/*")) {
296
+ const shut = rest.indexOf("*/", 2);
297
+ if (shut === -1)
298
+ break;
299
+ rest = rest.slice(shut + 2).trimStart();
300
+ }
301
+ return rest;
302
+ }
303
+ /** One past the last token in `[from, to)` — a code character, a literal or a URI; `from` when it holds none. */
304
+ export function tokenEnd(masked, from, to) {
305
+ let end = to;
306
+ while (end > from && /\s/.test(masked.code[end - 1]))
307
+ end -= 1;
308
+ for (const spans of [masked.literals, masked.uris]) {
309
+ // The last span ending by `to`: the lists are in order and never overlap.
310
+ let k = firstSpanIndex(spans, to) - 1;
311
+ while (k >= 0 && spans[k].end > to)
312
+ k -= 1;
313
+ if (k >= 0 && spans[k].start >= from)
314
+ end = Math.max(end, spans[k].end);
315
+ }
316
+ return end;
317
+ }
318
+ /**
319
+ * Where a bodyless statement whose head spans `head` ends: past its last token
320
+ * and whatever trails that on its line — a `// comment`, a closed `/* … *\/` —
321
+ * which is its own authored text and rides with it when the archive carries it.
322
+ *
323
+ * A block comment it OPENS there and closes on a later line is the exception,
324
+ * and it ends before that one. Ended inside it, the statement handed its
325
+ * readers a point inside a comment: placement spliced the next addition on
326
+ * the following line, still in the comment, and a bodyless parent gained its
327
+ * `{` there — the addition was commented out of a merge that parsed, written
328
+ * at exit 0 (probed on main at 171555d).
329
+ */
330
+ export function statementEnd(text, masked, head) {
331
+ const last = tokenEnd(masked, head.start, head.end);
332
+ if (pastComments(text.slice(last, head.end)).startsWith("/*"))
333
+ return last;
334
+ let end = head.end;
335
+ while (end > head.start && /\s/.test(text[end - 1]))
336
+ end -= 1;
337
+ return end;
338
+ }
339
+ /** Whether a span starts exactly at `at`. */
340
+ function spanAt(spans, at) {
341
+ return firstSpanFrom(spans, at)?.start === at;
342
+ }
343
+ /** Whether a span starts inside `[from, end)`. */
344
+ function spanIn(spans, range) {
345
+ const s = firstSpanFrom(spans, range.from);
346
+ return s !== undefined && s.start < range.end;
347
+ }
348
+ /** The first span starting at or after `at`. */
349
+ function firstSpanFrom(spans, at) {
350
+ return spans[firstSpanIndex(spans, at)];
351
+ }
352
+ /** The index of the first span starting at or after `at` — `maskSource` records each list in order, so a binary search. */
353
+ function firstSpanIndex(spans, at) {
354
+ let lo = 0;
355
+ let hi = spans.length;
356
+ while (lo < hi) {
357
+ const mid = (lo + hi) >> 1;
358
+ if (spans[mid].start < at)
359
+ lo = mid + 1;
360
+ else
361
+ hi = mid;
362
+ }
363
+ return lo;
364
+ }
81
365
  /** An element declaration located in source, under the id the computed model gives it. */
82
366
  //# sourceMappingURL=source-mask.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"source-mask.js","sourceRoot":"","sources":["../../../src/core/c4/source-mask.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Masking LikeC4 source, so a brace count can be trusted.\n *\n * Split from `likec4.ts` because it is the one half of the text-level view that\n * knows nothing about the model: it reads bytes and reports spans, and the\n * scanner above it turns those spans into declarations. `archive`'s splice\n * needs both, and needs them to agree — a mask that misses a literal makes\n * `matchBrace` close a block inside a string, and the merge writes into the\n * middle of somebody's description.\n */\nexport interface SourceLiteral {\n start: number;\n end: number;\n value: string;\n}\n\n/**\n * A LikeC4 source with everything that is not code blanked out: string\n * interiors and comments become spaces, newlines survive, and the result is\n * exactly as long as the input — so structure (braces, arrows, `#tags`) can be\n * read with plain string operations at offsets that are valid in the original.\n */\nexport interface MaskedSource {\n code: string;\n literals: SourceLiteral[];\n}\n\nexport function maskSource(text: string): MaskedSource {\n const out = text.split(\"\");\n const literals: SourceLiteral[] = [];\n type State = \"code\" | \"squote\" | \"dquote\" | \"lineComment\" | \"blockComment\";\n let state: State = \"code\";\n let litStart = -1;\n let lit = \"\";\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i]!;\n const next = text[i + 1];\n switch (state) {\n case \"code\":\n if (ch === \"'\" || ch === '\"') {\n state = ch === \"'\" ? \"squote\" : \"dquote\";\n litStart = i;\n lit = \"\";\n out[i] = \" \";\n } else if (ch === \"/\" && next === \"/\") {\n state = \"lineComment\";\n out[i] = \" \";\n } else if (ch === \"/\" && next === \"*\") {\n state = \"blockComment\";\n out[i] = \" \";\n }\n break;\n case \"squote\":\n case \"dquote\":\n if (ch === \"\\\\\" && i + 1 < text.length) {\n lit += text[i + 1]!;\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 1;\n } else if (ch === (state === \"squote\" ? \"'\" : '\"')) {\n literals.push({ start: litStart, end: i + 1, value: lit });\n state = \"code\";\n out[i] = \" \";\n } else {\n lit += ch;\n if (ch !== \"\\n\") out[i] = \" \";\n }\n break;\n case \"lineComment\":\n if (ch === \"\\n\") state = \"code\";\n else out[i] = \" \";\n break;\n case \"blockComment\":\n if (ch === \"*\" && next === \"/\") {\n state = \"code\";\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 1;\n } else if (ch !== \"\\n\") {\n out[i] = \" \";\n }\n break;\n }\n }\n return { code: out.join(\"\"), literals };\n}\n\n/** Index of the `}` matching the `{` at `open`, over MASKED code; -1 when unbalanced. */\nexport function matchBrace(code: string, open: number): number {\n let depth = 0;\n for (let i = open; i < code.length; i += 1) {\n if (code[i] === \"{\") depth += 1;\n else if (code[i] === \"}\") {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\n/** An element declaration located in source, under the id the computed model gives it. */\n"]}
1
+ {"version":3,"file":"source-mask.js","sourceRoot":"","sources":["../../../src/core/c4/source-mask.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Masking LikeC4 source, so a brace count can be trusted.\n *\n * Split from `likec4.ts` because it is the one half of the text-level view that\n * knows nothing about the model: it reads bytes and reports spans, and the\n * scanner above it turns those spans into declarations. `archive`'s splice\n * needs both, and needs them to agree — a mask that misses a literal makes\n * `matchBrace` close a block inside a string, and the merge writes into the\n * middle of somebody's description.\n *\n * `statementHead` and `statementEnd` are the readings of GRAMMAR here — where a\n * statement's head ends, and where a bodyless statement ends past it: questions\n * about tokens rather than declarations — and they sit beside the mask because\n * they read nothing but the bytes, the mask and the literal and URI lists;\n * which declaration the head is stays the scanner's to say.\n */\n/** A half-open `[start, end)` run of the source. */\nexport interface SourceSpan {\n start: number;\n end: number;\n}\n\nexport interface SourceLiteral extends SourceSpan {\n value: string;\n}\n\n/**\n * A LikeC4 source with everything that is not code blanked out: string\n * interiors, URIs and comments become spaces, newlines survive, and the result\n * is exactly as long as the input — so structure (braces, arrows, `#tags`) can\n * be read with plain string operations at offsets that are valid in the original.\n *\n * A blank is not nothing, which is why the literals and the URIs are listed\n * beside it: both are TOKENS the grammar reads, and a reader that only sees the\n * spaces reads `description 'x'` as the word `description` alone. Comments are\n * listed nowhere, because to the grammar they are exactly the blank they become.\n */\nexport interface MaskedSource {\n code: string;\n literals: SourceLiteral[];\n /** Every URI token (`uriEnd`), in document order. */\n uris: SourceSpan[];\n}\n\n/**\n * LikeC4's MARKDOWN string: `'''…'''` or `\"\"\"…\"\"\"`, closed by the first\n * matching triple and read with no escapes (the grammar's `MarkdownString` is\n * an until-token, not a quoted run). It is what a long `description` is\n * written in, and the reason it is its own state rather than three ordinary\n * quotes: read as `''` + `'…'` + `''`, the first apostrophe or odd quote in the\n * prose — \"it's\" — ended the literal, every brace after it was CODE, and the\n * rest of the document was read inside out. A map with one such description\n * above its model had no model block at all as far as the splice map could\n * tell, and archive refused a landscape LikeC4 parses cleanly.\n */\nconst MARKDOWN_QUOTES = [\"'''\", '\"\"\"'] as const;\n\n/**\n * Where a URI token starting at `at` ends, or -1 when none starts there.\n *\n * LikeC4 lexes `scheme://…` and a relative `./…`, `../…` or `/…` path as ONE\n * token running to the next whitespace — tried before the comment tokens can\n * see the `//` inside it, because the token starts at the scheme, not at the\n * slash. Read as code, `link https://…` opened a line comment at the `//`, and\n * that blanked whatever followed on the line: `x = kind 'X' { link\n * https://… }` lost its closing brace, and the scan found no model block in a\n * map LikeC4 parses. A URI's interior is never structure — a `{id}`, a `#anchor`,\n * an apostrophe are all part of the token — so it is blanked like a literal's.\n * It is recorded in `uris`, not among the literals, because it is no string —\n * no title is read from it — but it IS a token: blanked and recorded nowhere,\n * `link https://…` read as the bare word `link`, and `statementHead` ran that\n * on into the next line and swallowed the declaration written there.\n */\nfunction uriEnd(text: string, at: number): number {\n if (at > 0 && /[\\w.-]/.test(text[at - 1]!)) return -1;\n let j = at;\n if (/\\w/.test(text[at]!)) {\n while (j < text.length && /\\w/.test(text[j]!)) j += 1;\n if (!text.startsWith(\"://\", j)) return -1;\n j += 3;\n } else {\n while (j < at + 2 && text[j] === \".\") j += 1;\n const after = text[j + 1];\n // `//` and `/*` with no dot before them are the comment tokens, which win there.\n if (text[j] !== \"/\" || after === undefined || after === \"/\" || /\\s/.test(after) || (j === at && after === \"*\")) return -1;\n }\n while (j < text.length && !/\\s/.test(text[j]!)) j += 1;\n return j;\n}\n\nexport function maskSource(text: string): MaskedSource {\n const out = text.split(\"\");\n const literals: SourceLiteral[] = [];\n const uris: SourceSpan[] = [];\n type State = \"code\" | \"squote\" | \"dquote\" | \"markdown\" | \"lineComment\" | \"blockComment\";\n let state: State = \"code\";\n let litStart = -1;\n let lit = \"\";\n let fence = \"\";\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i]!;\n const next = text[i + 1];\n switch (state) {\n case \"code\": {\n const uri = uriEnd(text, i);\n if (uri !== -1) {\n uris.push({ start: i, end: uri });\n for (; i < uri; i += 1) out[i] = \" \";\n i -= 1;\n break;\n }\n fence = MARKDOWN_QUOTES.find((q) => text.startsWith(q, i)) ?? \"\";\n if (fence !== \"\") {\n state = \"markdown\";\n litStart = i;\n lit = \"\";\n out[i] = out[i + 1] = out[i + 2] = \" \";\n i += 2;\n } else if (ch === \"'\" || ch === '\"') {\n state = ch === \"'\" ? \"squote\" : \"dquote\";\n litStart = i;\n lit = \"\";\n out[i] = \" \";\n } else if (ch === \"/\" && next === \"/\") {\n state = \"lineComment\";\n out[i] = \" \";\n } else if (ch === \"/\" && next === \"*\") {\n state = \"blockComment\";\n out[i] = \" \";\n }\n break;\n }\n case \"squote\":\n case \"dquote\":\n if (ch === \"\\\\\" && i + 1 < text.length) {\n lit += text[i + 1]!;\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 1;\n } else if (ch === (state === \"squote\" ? \"'\" : '\"')) {\n literals.push({ start: litStart, end: i + 1, value: lit });\n state = \"code\";\n out[i] = \" \";\n } else {\n lit += ch;\n if (ch !== \"\\n\") out[i] = \" \";\n }\n break;\n case \"markdown\":\n if (text.startsWith(fence, i)) {\n literals.push({ start: litStart, end: i + 3, value: lit });\n state = \"code\";\n out[i] = out[i + 1] = out[i + 2] = \" \";\n i += 2;\n } else {\n lit += ch;\n if (ch !== \"\\n\") out[i] = \" \";\n }\n break;\n case \"lineComment\":\n // LikeC4's line comment is `//[^\\n\\r]*`: a bare `\\r` ends it. Masked to\n // the `\\n`, `a -> b // why` with a bare `\\r` before the next edge hid\n // that edge — `validate --all --base` never graded its op, and archive\n // refused the map on a relationship count.\n if (ch === \"\\n\" || ch === \"\\r\") state = \"code\";\n else out[i] = \" \";\n break;\n case \"blockComment\":\n if (ch === \"*\" && next === \"/\") {\n state = \"code\";\n out[i] = \" \";\n out[i + 1] = \" \";\n i += 1;\n } else if (ch !== \"\\n\") {\n out[i] = \" \";\n }\n break;\n }\n }\n return { code: out.join(\"\"), literals, uris };\n}\n\n/** Index of the `}` matching the `{` at `open`, over MASKED code; -1 when unbalanced. */\nexport function matchBrace(code: string, open: number): number {\n let depth = 0;\n for (let i = open; i < code.length; i += 1) {\n if (code[i] === \"{\") depth += 1;\n else if (code[i] === \"}\") {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\n/**\n * Every TOP-LEVEL block in MASKED code whose opening `re` matches — `re` is\n * global and its match ends at the block's `{` — with the block's body, read\n * to end of file when the brace never closes. Only depth zero counts: a block\n * nested inside another is not a declaration LikeC4 would honour. Depth is a\n * plain count, because the mask has already blanked every brace inside a\n * string or a comment — the one thing that would make counting lie — and it\n * is tracked in one forward pass rather than recounted per match.\n */\nexport function* topLevelBlocks(code: string, re: RegExp): Generator<{ match: RegExpExecArray; body: string }> {\n let depth = 0;\n let cursor = 0;\n for (const match of code.matchAll(re)) {\n for (; cursor < match.index; cursor += 1) {\n if (code[cursor] === \"{\") depth += 1;\n else if (code[cursor] === \"}\") depth -= 1;\n }\n if (depth !== 0) continue;\n const open = match.index + match[0].length - 1;\n const close = matchBrace(code, open);\n yield { match, body: code.slice(open + 1, close === -1 ? code.length : close) };\n }\n}\n\n/** A statement's head in MASKED code: where it ends, and the `{` opening its body (-1 when it has none). */\nexport interface StatementHead {\n end: number;\n opener: number;\n}\n\n/**\n * The head of the statement starting at `from`, inside a block that closes at\n * `close`: up to its own `{`, the enclosing block's `}`, or the end of its\n * line — unless LikeC4 reads on.\n *\n * It does read on, because its newlines are HIDDEN tokens (`NL` in the\n * grammar): a declaration may be split across lines anywhere a space could\n * go. A line-bounded head made each split form either invisible — so archive\n * refused a valid map — or, worse, MISREAD: `name = kind 'title'` with its `{`\n * on the next line read as a bodyless element, and a neighbour spliced in after\n * that line took the body for its own, a re-parse that passes over a merge\n * that moved another element's description and children (probed 2026-10-20).\n * The next line continues the statement when its first token can only belong\n * to it:\n *\n * - a `{` — a body opens nothing else;\n * - a string — no LikeC4 statement starts with one, so it is this one's\n * title, description or technology;\n * - a `#tag` — a relationship's tags may follow it on the next line, and an\n * element's tags live inside its body, never after its head;\n * - anything at all, when the head so far cannot end a statement: it ends in\n * `=` or `->`, or it is one bare name (`entityService` above `= kind`, a\n * kind above the name it declares, `link` above its URI).\n *\n * A line starting `->` after a COMPLETE head is a statement of its own — the\n * sourceless edge from the enclosing element — which is why an arrow at the\n * start of a line continues only a bare name.\n *\n * \"Bare\" is read off the tokens, not the blanks: `link https://…` and\n * `description 'x'` are masked to one word each, and both are complete\n * properties. Read as bare names they ran on into the next line, and whatever\n * was declared there — a child, the parent's sourceless edge — was read into\n * the property and lost (2e490dd blanked the URI and recorded nothing).\n *\n * A line ends at a carriage return as well as a line feed: LikeC4's `NL` is\n * `[\\r\\n]+`, so a bare `\\r` is a line break to it. Ended at `\\n` alone, the\n * head of `link https://…`, `description 'x'` or `technology 'TS'` ran on over\n * a bare `\\r` into the parent's sourceless edge, which was read as an edge\n * FROM `link` (`description`, `technology`): the counts still agreed, nothing\n * refused, and `validate --all --base` graded the branch's misspelt op nowhere\n * (probed on main at 171555d). A CRLF line ends at its `\\r` now rather than\n * its `\\n`, which nothing reads differently — `nextToken` steps over both.\n */\nexport function statementHead(masked: MaskedSource, from: number, close: number): StatementHead {\n const { code } = masked;\n let j = from;\n for (;;) {\n while (j < close && code[j] !== \"\\n\" && code[j] !== \"\\r\" && code[j] !== \"{\" && code[j] !== \"}\") j += 1;\n if (j >= close) return { end: close, opener: -1 };\n if (code[j] === \"{\") return { end: j, opener: j };\n if (code[j] === \"}\") return { end: j, opener: -1 };\n const next = nextToken(masked, j, close);\n if (next === -1 || !readsOn(masked, { from, end: j }, next)) return { end: j, opener: -1 };\n j = next;\n }\n}\n\n/**\n * The first token after `at`: a literal's or a URI's start, or a non-blank\n * character of code; -1 at the block's close. A URI is masked blank, so\n * without its span `link` above its URI looked past the URI to the line after\n * — and ran on into it.\n */\nfunction nextToken(masked: MaskedSource, at: number, close: number): number {\n for (let k = at; k < close; k += 1) {\n if (spanAt(masked.literals, k) || spanAt(masked.uris, k) || !/\\s/.test(masked.code[k]!)) return k;\n }\n return -1;\n}\n\n/** Whether the token at `next` belongs to the statement whose head so far spans `[from, end)`. */\nfunction readsOn(masked: MaskedSource, head: { from: number; end: number }, next: number): boolean {\n const { code, literals, uris } = masked;\n if (spanAt(literals, next)) return true;\n if (code[next] === \"{\" || code[next] === \"#\") return true;\n if (code[next] === \"}\") return false;\n const sofar = code.slice(head.from, head.end).trim();\n if (/(?:=|->)$/.test(sofar)) return true;\n // Masked, `description 'x'` reads as the one word `description` and\n // `link https://…` as the one word `link`: a head holding a literal or a URI\n // is never a bare name.\n return !spanIn(literals, head) && !spanIn(uris, head) && /^[A-Za-z_][\\w.-]*$/.test(sofar);\n}\n\n/**\n * Source text from a point in CODE, past its leading blanks and every block\n * comment that closes within it: what is left starts with code, with a line\n * comment, with a block comment the text does not close — or is empty. Read\n * off the raw text, because the mask blanks a comment exactly like a string's\n * interior, and the splice must tell the two apart: a comment trailing a\n * statement is room to splice past, a string is not.\n */\nexport function pastComments(text: string): string {\n let rest = text.trimStart();\n while (rest.startsWith(\"/*\")) {\n const shut = rest.indexOf(\"*/\", 2);\n if (shut === -1) break;\n rest = rest.slice(shut + 2).trimStart();\n }\n return rest;\n}\n\n/** One past the last token in `[from, to)` — a code character, a literal or a URI; `from` when it holds none. */\nexport function tokenEnd(masked: MaskedSource, from: number, to: number): number {\n let end = to;\n while (end > from && /\\s/.test(masked.code[end - 1]!)) end -= 1;\n for (const spans of [masked.literals, masked.uris]) {\n // The last span ending by `to`: the lists are in order and never overlap.\n let k = firstSpanIndex(spans, to) - 1;\n while (k >= 0 && spans[k]!.end > to) k -= 1;\n if (k >= 0 && spans[k]!.start >= from) end = Math.max(end, spans[k]!.end);\n }\n return end;\n}\n\n/**\n * Where a bodyless statement whose head spans `head` ends: past its last token\n * and whatever trails that on its line — a `// comment`, a closed `/* … *\\/` —\n * which is its own authored text and rides with it when the archive carries it.\n *\n * A block comment it OPENS there and closes on a later line is the exception,\n * and it ends before that one. Ended inside it, the statement handed its\n * readers a point inside a comment: placement spliced the next addition on\n * the following line, still in the comment, and a bodyless parent gained its\n * `{` there — the addition was commented out of a merge that parsed, written\n * at exit 0 (probed on main at 171555d).\n */\nexport function statementEnd(text: string, masked: MaskedSource, head: SourceSpan): number {\n const last = tokenEnd(masked, head.start, head.end);\n if (pastComments(text.slice(last, head.end)).startsWith(\"/*\")) return last;\n let end = head.end;\n while (end > head.start && /\\s/.test(text[end - 1]!)) end -= 1;\n return end;\n}\n\n/** Whether a span starts exactly at `at`. */\nfunction spanAt(spans: readonly SourceSpan[], at: number): boolean {\n return firstSpanFrom(spans, at)?.start === at;\n}\n\n/** Whether a span starts inside `[from, end)`. */\nfunction spanIn(spans: readonly SourceSpan[], range: { from: number; end: number }): boolean {\n const s = firstSpanFrom(spans, range.from);\n return s !== undefined && s.start < range.end;\n}\n\n/** The first span starting at or after `at`. */\nfunction firstSpanFrom(spans: readonly SourceSpan[], at: number): SourceSpan | undefined {\n return spans[firstSpanIndex(spans, at)];\n}\n\n/** The index of the first span starting at or after `at` — `maskSource` records each list in order, so a binary search. */\nfunction firstSpanIndex(spans: readonly SourceSpan[], at: number): number {\n let lo = 0;\n let hi = spans.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (spans[mid]!.start < at) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\n/** An element declaration located in source, under the id the computed model gives it. */\n"]}