@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.2

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 (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * One directory of `.likec4` documents, loaded as ONE LikeC4 project — the way
3
+ * the renderer already reads `architecture/`.
4
+ *
5
+ * A package of its own rather than more of `../workspace.ts`, and the seam is
6
+ * the isolation rule, not the line count. That module stages every document as
7
+ * its own single-file project ON PURPOSE, so an author-written `import` can
8
+ * never resolve against a sibling; this one does the opposite deliberately, for
9
+ * the set of documents that are meant to see each other. Two loaders with
10
+ * opposite contracts should not share a file: the next reader has to be able to
11
+ * tell at a glance which one they are looking at.
12
+ */
13
+ import { randomBytes } from "node:crypto";
14
+ import { copyFile, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
15
+ import { tmpdir } from "node:os";
16
+ import { dirname, join, relative, resolve } from "node:path";
17
+ import { LikeC4 } from "likec4";
18
+ import { inOrder } from "../../kernel/concurrency.js";
19
+ import { flattenModel } from "../likec4.js";
20
+ import { readDynamicViews } from "../parsed/dynamic-views.js";
21
+ import { readSpecification } from "../parsed/specification.js";
22
+ import { readViewIds } from "../parsed/view-ids.js";
23
+ import { readDeployment, NO_DEPLOYMENT } from "../parsed/deployment.js";
24
+ /**
25
+ * Stage a set of documents as ONE project, mirroring their paths relative to
26
+ * `base`.
27
+ *
28
+ * The relative structure is preserved rather than flattened to basenames, and
29
+ * that is load-bearing twice over: two use cases named `checkout.likec4` in
30
+ * different subdirectories would overwrite each other on copy, and LikeC4
31
+ * reports a view's `sourcePath` relative to the project root — so flattening
32
+ * would make every finding name a file the author cannot find.
33
+ */
34
+ async function stageProject(root, base, targets) {
35
+ const project = `arch_${randomBytes(8).toString("hex")}`;
36
+ const folder = join(root, project);
37
+ await mkdir(folder, { recursive: true });
38
+ await writeFile(join(folder, "likec4.config.json"), JSON.stringify({ name: project }), "utf8");
39
+ // rel -> real path, so an error carrying the staged path can be handed back
40
+ // spelled the way the author wrote it.
41
+ const byRel = new Map();
42
+ await inOrder(targets, async (path) => {
43
+ const rel = relative(base, path).split(/[\\/]/).join("/");
44
+ const dest = join(folder, ...rel.split("/"));
45
+ try {
46
+ await mkdir(dirname(dest), { recursive: true });
47
+ await copyFile(path, dest);
48
+ byRel.set(rel, path);
49
+ }
50
+ catch {
51
+ // Dropped, exactly as `stage` drops one: the caller's ordinary per-path
52
+ // load owns the error, and a project missing one document still answers
53
+ // about the rest.
54
+ }
55
+ return null;
56
+ });
57
+ return byRel;
58
+ }
59
+ /**
60
+ * Load a directory of `.likec4` documents as ONE LikeC4 project — the way the
61
+ * renderer already reads `architecture/`, and the only way a document that
62
+ * declares views over ANOTHER file's elements can be read at all.
63
+ *
64
+ * Measured at the 1.59.2 pin: a views-only `usecases/checkout.likec4` parsed
65
+ * alone is five errors (unresolved tag, unresolved elements); the same file
66
+ * beside its landscape in one project is zero, and each view comes back with
67
+ * its own `sourcePath`. That is the whole reason this function exists — a use
68
+ * case lives in a file of its own, and `loadBatch`'s one-project-per-document
69
+ * isolation makes such a file unreadable by construction.
70
+ *
71
+ * Two rules the caller must know, because both invert what holds elsewhere:
72
+ *
73
+ * - **Exactly one document in the set declares the `specification` block.**
74
+ * Every `.likec4` file loam parses alone must declare its own; inside one
75
+ * project a second declaration is a duplicate error blamed on BOTH files
76
+ * (measured).
77
+ * - **Errors are per document, but the MODEL is all-or-nothing.** A typo in
78
+ * one file is attributed to that file — so the finding names it, which is
79
+ * better than today, where a use case in the landscape would blame the
80
+ * landscape — but loam's standing rule is that errors mean no model, and
81
+ * here the model is the project's. `clean` says which case the caller is in.
82
+ *
83
+ * The generated `architecture/subsystems.likec4` must NOT be in `targets`:
84
+ * docs/DESIGN.md rule 26 keeps it a byte compare, and a stale one whose
85
+ * `include` names a removed element would otherwise be a parse error that
86
+ * blanks the whole fleet map.
87
+ */
88
+ export async function loadProject(base, paths) {
89
+ const empty = (errors, clean) => ({
90
+ errors,
91
+ clean,
92
+ elements: [],
93
+ relationships: [],
94
+ views: [],
95
+ viewIds: [],
96
+ deployment: NO_DEPLOYMENT,
97
+ });
98
+ const targets = [...new Set(paths.map((path) => resolve(path)))];
99
+ if (targets.length === 0)
100
+ return empty(new Map(), true);
101
+ const root = await mkdtemp(join(tmpdir(), "loam-c4-"));
102
+ try {
103
+ const byRel = await stageProject(root, resolve(base), targets);
104
+ if (byRel.size === 0)
105
+ return empty(new Map(), true);
106
+ const likec4 = await LikeC4.fromWorkspace(root, { logger: false });
107
+ try {
108
+ const errors = groupProjectErrors(likec4, byRel);
109
+ if (errors.size > 0)
110
+ return empty(errors, false);
111
+ const model = (await likec4.parsedModel());
112
+ return {
113
+ errors,
114
+ clean: true,
115
+ specification: readSpecification(model.specification),
116
+ views: readDynamicViews(model),
117
+ viewIds: readViewIds(model),
118
+ deployment: readDeployment(model),
119
+ ...flattenModel(model),
120
+ };
121
+ }
122
+ finally {
123
+ await likec4.dispose();
124
+ }
125
+ }
126
+ finally {
127
+ await rm(root, { recursive: true, force: true });
128
+ }
129
+ }
130
+ /**
131
+ * `getErrors()` split per DOCUMENT inside one project, each error's
132
+ * `sourceFsPath` rewritten to the document's real absolute path.
133
+ *
134
+ * `groupErrors` above keys on the project folder segment, which cannot work
135
+ * here — every document shares one project. This keys on the staged relative
136
+ * path instead, longest first so `usecases/checkout.likec4` is matched before a
137
+ * hypothetical `checkout.likec4` at the root. An error nobody can attribute
138
+ * throws for the same reason it does there: a dropped error grades its document
139
+ * clean, which is failing open in the fleet gate.
140
+ */
141
+ function groupProjectErrors(likec4, byRel) {
142
+ const rels = [...byRel.keys()].sort((a, b) => b.length - a.length);
143
+ const grouped = new Map();
144
+ for (const err of likec4.getErrors()) {
145
+ const spelled = err.sourceFsPath.split(/[\\/]/).join("/");
146
+ const rel = rels.find((candidate) => spelled.endsWith(`/${candidate}`) || spelled === candidate);
147
+ if (rel === undefined) {
148
+ throw new Error(`unattributable LikeC4 error in project workspace: ${err.message}`);
149
+ }
150
+ const real = byRel.get(rel);
151
+ const list = grouped.get(real) ?? [];
152
+ list.push({ ...err, sourceFsPath: real });
153
+ grouped.set(real, list);
154
+ }
155
+ return grouped;
156
+ }
157
+ /**
158
+ * A `ProjectDoc` in the shape every existing landscape reader already takes.
159
+ *
160
+ * The per-file error map flattens into one list, which loses nothing: each
161
+ * error still carries the real path of the document that raised it, so a
162
+ * finding can name the file even though the list is flat. Sorted by that path
163
+ * so a project with two broken documents reports them in a stable order —
164
+ * `getErrors()` returns whatever order the workspace scan produced.
165
+ *
166
+ * The model travels only when the project is clean, which is loam's standing
167
+ * rule (`errors mean no model`) applied at project altitude: a use-case file
168
+ * with an unresolved element leaves the landscape unusable, and pretending
169
+ * otherwise would grade a fleet against half a map.
170
+ */
171
+ export function asLoadedDoc(doc) {
172
+ const errors = [...doc.errors]
173
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
174
+ .flatMap(([, list]) => list);
175
+ if (!doc.clean)
176
+ return { errors, elements: [], relationships: [] };
177
+ return {
178
+ errors,
179
+ ...(doc.specification === undefined ? {} : { specification: doc.specification }),
180
+ views: doc.views,
181
+ viewIds: doc.viewIds,
182
+ deployment: doc.deployment,
183
+ elements: doc.elements,
184
+ relationships: doc.relationships,
185
+ };
186
+ }
187
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../../../src/core/c4/project/load.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One directory of `.likec4` documents, loaded as ONE LikeC4 project — the way\n * the renderer already reads `architecture/`.\n *\n * A package of its own rather than more of `../workspace.ts`, and the seam is\n * the isolation rule, not the line count. That module stages every document as\n * its own single-file project ON PURPOSE, so an author-written `import` can\n * never resolve against a sibling; this one does the opposite deliberately, for\n * the set of documents that are meant to see each other. Two loaders with\n * opposite contracts should not share a file: the next reader has to be able to\n * tell at a glance which one they are looking at.\n */\nimport { randomBytes } from \"node:crypto\";\nimport { copyFile, mkdir, mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { LikeC4 } from \"likec4\";\nimport { inOrder } from \"../../kernel/concurrency.js\";\nimport { flattenModel, type Elem, type LikeC4Error, type LoadedDoc, type ReadableModel, type Rel } from \"../likec4.js\";\nimport { readDynamicViews, type ParsedView } from \"../parsed/dynamic-views.js\";\nimport { readSpecification, type DocSpecification } from \"../parsed/specification.js\";\nimport { readViewIds, type ViewIdClaim } from \"../parsed/view-ids.js\";\nimport { readDeployment, NO_DEPLOYMENT, type DeploymentModel } from \"../parsed/deployment.js\";\n\n/** A directory of `.likec4` documents loaded as one LikeC4 project. */\nexport interface ProjectDoc {\n /** Errors by the REAL absolute path of the document that raised them. */\n errors: Map<string, LikeC4Error[]>;\n /** True when no document in the project raised one — loam's \"errors mean no model\" gate. */\n clean: boolean;\n elements: Elem[];\n relationships: Rel[];\n specification?: DocSpecification;\n /** Every dynamic view the project declares, each carrying the file it is written in. */\n views: ParsedView[];\n /** Every authored view id the project claims, each with its file. */\n viewIds: ViewIdClaim[];\n /** The project's deployment model — empty when it declares none, and when it did not parse. */\n deployment: DeploymentModel;\n}\n\n/**\n * Stage a set of documents as ONE project, mirroring their paths relative to\n * `base`.\n *\n * The relative structure is preserved rather than flattened to basenames, and\n * that is load-bearing twice over: two use cases named `checkout.likec4` in\n * different subdirectories would overwrite each other on copy, and LikeC4\n * reports a view's `sourcePath` relative to the project root — so flattening\n * would make every finding name a file the author cannot find.\n */\nasync function stageProject(root: string, base: string, targets: string[]): Promise<Map<string, string>> {\n const project = `arch_${randomBytes(8).toString(\"hex\")}`;\n const folder = join(root, project);\n await mkdir(folder, { recursive: true });\n await writeFile(join(folder, \"likec4.config.json\"), JSON.stringify({ name: project }), \"utf8\");\n // rel -> real path, so an error carrying the staged path can be handed back\n // spelled the way the author wrote it.\n const byRel = new Map<string, string>();\n await inOrder(targets, async (path) => {\n const rel = relative(base, path).split(/[\\\\/]/).join(\"/\");\n const dest = join(folder, ...rel.split(\"/\"));\n try {\n await mkdir(dirname(dest), { recursive: true });\n await copyFile(path, dest);\n byRel.set(rel, path);\n } catch {\n // Dropped, exactly as `stage` drops one: the caller's ordinary per-path\n // load owns the error, and a project missing one document still answers\n // about the rest.\n }\n return null;\n });\n return byRel;\n}\n\n/**\n * Load a directory of `.likec4` documents as ONE LikeC4 project — the way the\n * renderer already reads `architecture/`, and the only way a document that\n * declares views over ANOTHER file's elements can be read at all.\n *\n * Measured at the 1.59.2 pin: a views-only `usecases/checkout.likec4` parsed\n * alone is five errors (unresolved tag, unresolved elements); the same file\n * beside its landscape in one project is zero, and each view comes back with\n * its own `sourcePath`. That is the whole reason this function exists — a use\n * case lives in a file of its own, and `loadBatch`'s one-project-per-document\n * isolation makes such a file unreadable by construction.\n *\n * Two rules the caller must know, because both invert what holds elsewhere:\n *\n * - **Exactly one document in the set declares the `specification` block.**\n * Every `.likec4` file loam parses alone must declare its own; inside one\n * project a second declaration is a duplicate error blamed on BOTH files\n * (measured).\n * - **Errors are per document, but the MODEL is all-or-nothing.** A typo in\n * one file is attributed to that file — so the finding names it, which is\n * better than today, where a use case in the landscape would blame the\n * landscape — but loam's standing rule is that errors mean no model, and\n * here the model is the project's. `clean` says which case the caller is in.\n *\n * The generated `architecture/subsystems.likec4` must NOT be in `targets`:\n * docs/DESIGN.md rule 26 keeps it a byte compare, and a stale one whose\n * `include` names a removed element would otherwise be a parse error that\n * blanks the whole fleet map.\n */\nexport async function loadProject(base: string, paths: string[]): Promise<ProjectDoc> {\n const empty = (errors: Map<string, LikeC4Error[]>, clean: boolean): ProjectDoc => ({\n errors,\n clean,\n elements: [],\n relationships: [],\n views: [],\n viewIds: [],\n deployment: NO_DEPLOYMENT,\n });\n const targets = [...new Set(paths.map((path) => resolve(path)))];\n if (targets.length === 0) return empty(new Map(), true);\n const root = await mkdtemp(join(tmpdir(), \"loam-c4-\"));\n try {\n const byRel = await stageProject(root, resolve(base), targets);\n if (byRel.size === 0) return empty(new Map(), true);\n const likec4 = await LikeC4.fromWorkspace(root, { logger: false });\n try {\n const errors = groupProjectErrors(likec4, byRel);\n if (errors.size > 0) return empty(errors, false);\n const model = (await likec4.parsedModel()) as ReadableModel;\n return {\n errors,\n clean: true,\n specification: readSpecification(model.specification),\n views: readDynamicViews(model),\n viewIds: readViewIds(model),\n deployment: readDeployment(model),\n ...flattenModel(model),\n };\n } finally {\n await likec4.dispose();\n }\n } finally {\n await rm(root, { recursive: true, force: true });\n }\n}\n\n/**\n * `getErrors()` split per DOCUMENT inside one project, each error's\n * `sourceFsPath` rewritten to the document's real absolute path.\n *\n * `groupErrors` above keys on the project folder segment, which cannot work\n * here — every document shares one project. This keys on the staged relative\n * path instead, longest first so `usecases/checkout.likec4` is matched before a\n * hypothetical `checkout.likec4` at the root. An error nobody can attribute\n * throws for the same reason it does there: a dropped error grades its document\n * clean, which is failing open in the fleet gate.\n */\nfunction groupProjectErrors(likec4: LikeC4, byRel: Map<string, string>): Map<string, LikeC4Error[]> {\n const rels = [...byRel.keys()].sort((a, b) => b.length - a.length);\n const grouped = new Map<string, LikeC4Error[]>();\n for (const err of likec4.getErrors()) {\n const spelled = err.sourceFsPath.split(/[\\\\/]/).join(\"/\");\n const rel = rels.find((candidate) => spelled.endsWith(`/${candidate}`) || spelled === candidate);\n if (rel === undefined) {\n throw new Error(`unattributable LikeC4 error in project workspace: ${err.message}`);\n }\n const real = byRel.get(rel)!;\n const list = grouped.get(real) ?? [];\n list.push({ ...err, sourceFsPath: real });\n grouped.set(real, list);\n }\n return grouped;\n}\n\n/**\n * A `ProjectDoc` in the shape every existing landscape reader already takes.\n *\n * The per-file error map flattens into one list, which loses nothing: each\n * error still carries the real path of the document that raised it, so a\n * finding can name the file even though the list is flat. Sorted by that path\n * so a project with two broken documents reports them in a stable order —\n * `getErrors()` returns whatever order the workspace scan produced.\n *\n * The model travels only when the project is clean, which is loam's standing\n * rule (`errors mean no model`) applied at project altitude: a use-case file\n * with an unresolved element leaves the landscape unusable, and pretending\n * otherwise would grade a fleet against half a map.\n */\nexport function asLoadedDoc(doc: ProjectDoc): LoadedDoc {\n const errors = [...doc.errors]\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .flatMap(([, list]) => list);\n if (!doc.clean) return { errors, elements: [], relationships: [] };\n return {\n errors,\n ...(doc.specification === undefined ? {} : { specification: doc.specification }),\n views: doc.views,\n viewIds: doc.viewIds,\n deployment: doc.deployment,\n elements: doc.elements,\n relationships: doc.relationships,\n };\n}\n"]}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The `architecture/` project AS THIS FEATURE'S MERGE WOULD LEAVE IT, staged
3
+ * and parsed, with a feature's own documents standing beside it.
4
+ *
5
+ * WHY THE POST-MERGE MAP AND NOT THE LIVING ONE. A document a feature brings
6
+ * names elements, and a feature that adds a service adds the element its own
7
+ * document names. Graded against the LIVING landscape that name is unresolved —
8
+ * LikeC4 refuses the document, the project blanks, and loam reports a hole
9
+ * against a file whose only fault is arriving with the change that makes it
10
+ * true. Graded against the merge it resolves exactly when the merge lands the
11
+ * element it names, which is `ARCH-LOAM-FEATURE-CORPUS`: inside a feature
12
+ * window, the corpus is feature ∪ living.
13
+ *
14
+ * IT IS ALSO THE CHECK. The merge preview is the only reading under which "a
15
+ * document naming an element the merge does not land" is answerable at all, and
16
+ * that refusal is why it runs before `loam archive` writes rather than after:
17
+ * without it the document is copied into `architecture/` and the NEXT reader's
18
+ * `loam validate --all` carries the failure, against a file they did not write.
19
+ *
20
+ * ## Why it lives here rather than in one axis
21
+ *
22
+ * It was `core/usecases/delta/overlay.ts`'s private machinery while the
23
+ * use-case slot was the only feature-local `.likec4` document. The deployment
24
+ * slot is the second, and a second copy of this staging is the copy that
25
+ * drifts — the two would disagree first about the merge preview, which is the
26
+ * one thing both refusals rest on. So the staging moved down to the package
27
+ * that owns the project loader and the merge, and each axis keeps only its own
28
+ * INTERPRETATION of the parse: which views are the feature's, or which
29
+ * documents failed.
30
+ *
31
+ * ## Why it stages its own tree
32
+ *
33
+ * `loadProject` copies documents from disk, keyed on their path relative to a
34
+ * base — so it can be handed neither a landscape that exists only in memory nor
35
+ * a document living outside `architecture/`, and both are exactly what this
36
+ * needs. Teaching it to take content would put a second, rarely-exercised
37
+ * staging rule inside a loader whose docstring already carries two rules that
38
+ * invert what holds elsewhere. Staging here costs one copy of a handful of
39
+ * small files and keeps that loader's contract at one.
40
+ *
41
+ * THE PRICE OF THAT CHOICE IS PATHS, and it is paid here rather than left to
42
+ * the caller: every path LikeC4 reports — a parse error's `sourceFsPath`, a
43
+ * view's `sourcePath` — names a temp tree, and a finding naming a temp
44
+ * directory is a finding nobody can act on. `real()` maps them back to the file
45
+ * the author wrote, and the temp tree is gone before this returns.
46
+ *
47
+ * FAIL CLOSED, and not silently. Every failure is the `unreadable` arm carrying
48
+ * LikeC4's own messages: a document this cannot read is never one that grades
49
+ * clean.
50
+ */
51
+ import { existsSync } from "node:fs";
52
+ import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
53
+ import { tmpdir } from "node:os";
54
+ import { dirname, join, relative, resolve } from "node:path";
55
+ import { loadFile } from "../likec4.js";
56
+ import { planLandscapeMerge } from "../splice/landscape-merge.js";
57
+ import { inOrder } from "../../kernel/concurrency.js";
58
+ import { decodeDocument } from "../../kernel/document-bytes.js";
59
+ import { featurePaths, landscapePath, subsystemViewsPath } from "../../repo/paths.js";
60
+ import { architectureDir } from "./architecture.js";
61
+ import { architectureDocuments } from "./documents.js";
62
+ import { asLoadedDoc, loadProject } from "./load.js";
63
+ /**
64
+ * Stage the living `architecture/` with the merge preview in place of the
65
+ * landscape, add the feature's own documents, and parse the result.
66
+ *
67
+ * A feature bringing no documents is not this function's business: both callers
68
+ * return their own empty answer before reaching it, at the cost of one walk
69
+ * over a directory that is not there. That walk is the axis's per-feature
70
+ * opt-in and the whole price a fleet that has not adopted it pays.
71
+ */
72
+ export async function stageMergedProject(req) {
73
+ const merged = await mergedLandscape(req);
74
+ if (merged.kind === "failed")
75
+ return { kind: "unreadable", errors: [merged.reason] };
76
+ const arch = architectureDir(req.docsDir);
77
+ const living = await architectureDocuments(arch, [subsystemViewsPath(req.docsDir)]);
78
+ const root = await mkdtemp(join(tmpdir(), "loam-staged-"));
79
+ try {
80
+ // Staged rel -> the path the AUTHOR wrote, so every message that leaves
81
+ // here names a file they can open. Built as the tree is staged, because
82
+ // that is the one moment both spellings are in hand.
83
+ const authored = new Map();
84
+ const landscape = resolve(landscapePath(req.docsDir));
85
+ await inOrder(living, async (path) => {
86
+ // The living landscape is staged from the merge preview when there is
87
+ // one; copying it as well would declare every element twice.
88
+ if (merged.kind === "merged" && resolve(path) === landscape)
89
+ return null;
90
+ await stage(root, spell(relative(arch, path)), path, authored);
91
+ return null;
92
+ });
93
+ if (merged.kind === "merged") {
94
+ const rel = spell(relative(arch, landscape));
95
+ await write(root, rel, merged.content);
96
+ authored.set(rel, landscape);
97
+ }
98
+ // The feature's own documents LAST, so one whose living twin exists wins
99
+ // the staging rather than colliding with it. That case is already an error
100
+ // the gate refuses before any merge — `usecase.flow-exists`,
101
+ // `deployment.doc-exists` — and grading the feature's version is the more
102
+ // useful of the two answers to give an author who is about to be told to
103
+ // delete the file.
104
+ const mine = new Set();
105
+ for (const document of req.documents) {
106
+ await stage(root, document.rel, document.path, authored);
107
+ mine.add(document.rel);
108
+ }
109
+ const doc = asLoadedDoc(await loadProject(root, await architectureDocuments(root)));
110
+ // TWO SPELLINGS REACH HERE and both must map, which is the one thing this
111
+ // helper cannot be allowed to get half right. A parse error's
112
+ // `sourceFsPath` is an ABSOLUTE path into the temp tree; a view's
113
+ // `sourcePath` is already the project-RELATIVE rel LikeC4 reports. The
114
+ // rel-first lookup answers the second, and the relative() computation the
115
+ // first. Handling only one leaves the other silently returning the temp
116
+ // spelling, which is a finding naming a directory that no longer exists.
117
+ const real = (path) => authored.get(spell(path)) ?? authored.get(spell(relative(root, path))) ?? path;
118
+ if (doc.errors.length > 0) {
119
+ // An error naming no document is LikeC4 declining to attribute one. It
120
+ // still travels: a hole loam cannot place is still a hole, and dropping
121
+ // it here would grade the feature clean on the strength of a missing
122
+ // field.
123
+ return {
124
+ kind: "unreadable",
125
+ errors: doc.errors.map((e) => (e.sourceFsPath === undefined ? e.message : `${real(e.sourceFsPath)}: ${e.message}`)),
126
+ };
127
+ }
128
+ return { kind: "read", doc, real, mine };
129
+ }
130
+ catch (err) {
131
+ // The `unreadable` arm is a PROMISE, not a description of the happy path.
132
+ // Two things under here throw rather than return: `loadProject` refuses an
133
+ // error it cannot attribute to a document, and every `mkdir`/`copyFile` in
134
+ // the staging above can fail on a full disk or a denied temp directory.
135
+ // Callers run inside `loam validate --feature` and the archive gate — and
136
+ // the one behaviour a check added to those may never have is failing the
137
+ // run it was added to. So a throw becomes the same could-not-look answer a
138
+ // parse error already produces, which each caller turns into its own
139
+ // refusal that writes nothing.
140
+ return { kind: "unreadable", errors: [err instanceof Error ? err.message : String(err)] };
141
+ }
142
+ finally {
143
+ await rm(root, { recursive: true, force: true });
144
+ }
145
+ }
146
+ /**
147
+ * The merge preview, computed with the SAME function `loam archive` commits
148
+ * with.
149
+ *
150
+ * Reusing `planLandscapeMerge` rather than approximating it is what makes this
151
+ * an answer about the archive rather than a second opinion beside it: an
152
+ * element placed differently, a tag stripped differently, or a splice refused
153
+ * there and allowed here would be a document graded against a map the merge
154
+ * never writes.
155
+ *
156
+ * THE PARITY IS THE MERGE, NOT THE WHOLE GATE, and the difference is worth
157
+ * naming rather than leaving for a reader to discover. `planLandscape` runs one
158
+ * check BEFORE it computes the merge and this does not: a delta declaring the
159
+ * feature tag on a specification KIND is refused there, because every element
160
+ * of that kind inherits the tag and the merge would splice the whole document.
161
+ * Such a delta reaches `newEls` here unfiltered, so the preview this grades
162
+ * against is one the archive would never write. It changes no verdict — that
163
+ * archive refuses either way, before anything is written — and it is recorded
164
+ * because the next reader to widen either side needs to know the two are not
165
+ * the same gate.
166
+ *
167
+ * A throw is `failed` rather than propagated. Every `LandscapeSpliceError` this
168
+ * can raise is one `planLandscape` raises again — with its own message, its own
169
+ * refusal code, and before anything is written — so throwing here would only
170
+ * decide which of two identical refusals an author reads first.
171
+ */
172
+ async function mergedLandscape(req) {
173
+ const deltaPath = featurePaths(req.featureDir).delta;
174
+ const landscape = landscapePath(req.docsDir);
175
+ if (!existsSync(deltaPath) || !existsSync(landscape))
176
+ return { kind: "living" };
177
+ const delta = await (req.load ?? loadFile)(deltaPath);
178
+ if (delta.errors.length > 0) {
179
+ return {
180
+ kind: "failed",
181
+ reason: `delta.likec4 has ${delta.errors.length} parse error(s) — a feature's own documents cannot be graded against a map that does not parse`,
182
+ };
183
+ }
184
+ try {
185
+ const plan = await planLandscapeMerge({
186
+ landscapeText: await text(landscape),
187
+ deltaText: await text(deltaPath),
188
+ deltaElements: delta.elements,
189
+ newEls: delta.elements.filter((e) => e.tags.includes(req.featureId)),
190
+ newRels: delta.relationships.filter((r) => r.tags.includes(req.featureId)),
191
+ featureId: req.featureId,
192
+ });
193
+ return plan.content === null ? { kind: "living" } : { kind: "merged", content: plan.content };
194
+ }
195
+ catch (err) {
196
+ return { kind: "failed", reason: err instanceof Error ? err.message : String(err) };
197
+ }
198
+ }
199
+ /** Read a document loam is about to hand LikeC4, refusing the encodings that decode as empty. */
200
+ async function text(path) {
201
+ return decodeDocument(await readFile(path), path);
202
+ }
203
+ /** A path spelled the way a staged rel is spelled — `/`-separated, on every platform. */
204
+ function spell(path) {
205
+ return path.split(/[\\/]/).join("/");
206
+ }
207
+ /** Copy one authored document into the staged tree, remembering where it came from. */
208
+ async function stage(root, rel, path, authored) {
209
+ const dest = join(root, ...rel.split("/"));
210
+ await mkdir(dirname(dest), { recursive: true });
211
+ await copyFile(path, dest);
212
+ authored.set(rel, resolve(path));
213
+ }
214
+ /** Write one document into the staged tree from bytes loam holds rather than a file. */
215
+ async function write(root, rel, content) {
216
+ const dest = join(root, ...rel.split("/"));
217
+ await mkdir(dirname(dest), { recursive: true });
218
+ await writeFile(dest, content, "utf8");
219
+ }
220
+ //# sourceMappingURL=staged.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged.js","sourceRoot":"","sources":["../../../../src/core/c4/project/staged.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAkB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAwCrD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAyB;IAChE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAErF,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,wEAAwE;QACxE,wEAAwE;QACxE,qDAAqD;QACrD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC3C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACtD,MAAM,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACnC,sEAAsE;YACtE,6DAA6D;YAC7D,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACzE,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC/D,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;YAC7C,MAAM,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACvC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;QACD,yEAAyE;QACzE,2EAA2E;QAC3E,6DAA6D;QAC7D,0EAA0E;QAC1E,yEAAyE;QACzE,mBAAmB;QACnB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YACrC,MAAM,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACzD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QACD,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpF,0EAA0E;QAC1E,8DAA8D;QAC9D,kEAAkE;QAClE,uEAAuE;QACvE,0EAA0E;QAC1E,wEAAwE;QACxE,yEAAyE;QACzE,MAAM,IAAI,GAAG,CAAC,IAAY,EAAU,EAAE,CACpC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QACjF,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,SAAS;YACT,OAAO;gBACL,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;aACpH,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,wEAAwE;QACxE,0EAA0E;QAC1E,yEAAyE;QACzE,2EAA2E;QAC3E,qEAAqE;QACrE,+BAA+B;QAC/B,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;IAC5F,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,KAAK,UAAU,eAAe,CAAC,GAAyB;IACtD,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;IACrD,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,oBAAoB,KAAK,CAAC,MAAM,CAAC,MAAM,gGAAgG;SAChJ,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC;YACpC,aAAa,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC;YACpC,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC;YAChC,aAAa,EAAE,KAAK,CAAC,QAAQ;YAC7B,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACpE,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC1E,SAAS,EAAE,GAAG,CAAC,SAAS;SACzB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IAChG,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACtF,CAAC;AACH,CAAC;AAED,iGAAiG;AACjG,KAAK,UAAU,IAAI,CAAC,IAAY;IAC9B,OAAO,cAAc,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,yFAAyF;AACzF,SAAS,KAAK,CAAC,IAAY;IACzB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvC,CAAC;AAED,uFAAuF;AACvF,KAAK,UAAU,KAAK,CAAC,IAAY,EAAE,GAAW,EAAE,IAAY,EAAE,QAA6B;IACzF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,wFAAwF;AACxF,KAAK,UAAU,KAAK,CAAC,IAAY,EAAE,GAAW,EAAE,OAAe;IAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC","sourcesContent":["/**\n * The `architecture/` project AS THIS FEATURE'S MERGE WOULD LEAVE IT, staged\n * and parsed, with a feature's own documents standing beside it.\n *\n * WHY THE POST-MERGE MAP AND NOT THE LIVING ONE. A document a feature brings\n * names elements, and a feature that adds a service adds the element its own\n * document names. Graded against the LIVING landscape that name is unresolved —\n * LikeC4 refuses the document, the project blanks, and loam reports a hole\n * against a file whose only fault is arriving with the change that makes it\n * true. Graded against the merge it resolves exactly when the merge lands the\n * element it names, which is `ARCH-LOAM-FEATURE-CORPUS`: inside a feature\n * window, the corpus is feature ∪ living.\n *\n * IT IS ALSO THE CHECK. The merge preview is the only reading under which \"a\n * document naming an element the merge does not land\" is answerable at all, and\n * that refusal is why it runs before `loam archive` writes rather than after:\n * without it the document is copied into `architecture/` and the NEXT reader's\n * `loam validate --all` carries the failure, against a file they did not write.\n *\n * ## Why it lives here rather than in one axis\n *\n * It was `core/usecases/delta/overlay.ts`'s private machinery while the\n * use-case slot was the only feature-local `.likec4` document. The deployment\n * slot is the second, and a second copy of this staging is the copy that\n * drifts — the two would disagree first about the merge preview, which is the\n * one thing both refusals rest on. So the staging moved down to the package\n * that owns the project loader and the merge, and each axis keeps only its own\n * INTERPRETATION of the parse: which views are the feature's, or which\n * documents failed.\n *\n * ## Why it stages its own tree\n *\n * `loadProject` copies documents from disk, keyed on their path relative to a\n * base — so it can be handed neither a landscape that exists only in memory nor\n * a document living outside `architecture/`, and both are exactly what this\n * needs. Teaching it to take content would put a second, rarely-exercised\n * staging rule inside a loader whose docstring already carries two rules that\n * invert what holds elsewhere. Staging here costs one copy of a handful of\n * small files and keeps that loader's contract at one.\n *\n * THE PRICE OF THAT CHOICE IS PATHS, and it is paid here rather than left to\n * the caller: every path LikeC4 reports — a parse error's `sourceFsPath`, a\n * view's `sourcePath` — names a temp tree, and a finding naming a temp\n * directory is a finding nobody can act on. `real()` maps them back to the file\n * the author wrote, and the temp tree is gone before this returns.\n *\n * FAIL CLOSED, and not silently. Every failure is the `unreadable` arm carrying\n * LikeC4's own messages: a document this cannot read is never one that grades\n * clean.\n */\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { loadFile, type LoadedDoc } from \"../likec4.js\";\nimport { planLandscapeMerge } from \"../splice/landscape-merge.js\";\nimport { inOrder } from \"../../kernel/concurrency.js\";\nimport { decodeDocument } from \"../../kernel/document-bytes.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport { featurePaths, landscapePath, subsystemViewsPath } from \"../../repo/paths.js\";\nimport { architectureDir } from \"./architecture.js\";\nimport { architectureDocuments } from \"./documents.js\";\nimport { asLoadedDoc, loadProject } from \"./load.js\";\n\n/** One document a feature brings, and where it would live under `architecture/`. */\nexport interface StagedDocument {\n /** Its path relative to `architecture/`, `/`-separated — where the merge would write it. */\n rel: string;\n /** The absolute path of the authored file. */\n path: string;\n}\n\nexport interface StagedProjectRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n /** The feature's id — the tag that selects the delta's own additions. */\n featureId: string;\n /** The feature's own documents, staged LAST so they win over a living twin. */\n documents: readonly StagedDocument[];\n /**\n * The caller's memoised LikeC4 read, when it has one. Only `delta.likec4`\n * goes through it, and only to compute the merge preview — but a caller\n * inside `featureCoherence` has already loaded that exact document, and\n * loading one spins a fresh Langium workspace, which\n * `core/coherence/coherence.ts` calls the dominant per-feature cost.\n */\n load?: (path: string) => Promise<LoadedDoc>;\n}\n\n/** The staged parse, or the honest refusal to say. */\nexport type StagedProject =\n | {\n kind: \"read\";\n /** The whole staged project, parsed. */\n doc: LoadedDoc;\n /** A path LikeC4 reported, spelled as the author wrote it. */\n real: (path: string) => string;\n /** The staged rels that are THIS feature's documents — everything else is living. */\n mine: ReadonlySet<string>;\n }\n | { kind: \"unreadable\"; errors: string[] };\n\n/**\n * Stage the living `architecture/` with the merge preview in place of the\n * landscape, add the feature's own documents, and parse the result.\n *\n * A feature bringing no documents is not this function's business: both callers\n * return their own empty answer before reaching it, at the cost of one walk\n * over a directory that is not there. That walk is the axis's per-feature\n * opt-in and the whole price a fleet that has not adopted it pays.\n */\nexport async function stageMergedProject(req: StagedProjectRequest): Promise<StagedProject> {\n const merged = await mergedLandscape(req);\n if (merged.kind === \"failed\") return { kind: \"unreadable\", errors: [merged.reason] };\n\n const arch = architectureDir(req.docsDir);\n const living = await architectureDocuments(arch, [subsystemViewsPath(req.docsDir)]);\n const root = await mkdtemp(join(tmpdir(), \"loam-staged-\"));\n try {\n // Staged rel -> the path the AUTHOR wrote, so every message that leaves\n // here names a file they can open. Built as the tree is staged, because\n // that is the one moment both spellings are in hand.\n const authored = new Map<string, string>();\n const landscape = resolve(landscapePath(req.docsDir));\n await inOrder(living, async (path) => {\n // The living landscape is staged from the merge preview when there is\n // one; copying it as well would declare every element twice.\n if (merged.kind === \"merged\" && resolve(path) === landscape) return null;\n await stage(root, spell(relative(arch, path)), path, authored);\n return null;\n });\n if (merged.kind === \"merged\") {\n const rel = spell(relative(arch, landscape));\n await write(root, rel, merged.content);\n authored.set(rel, landscape);\n }\n // The feature's own documents LAST, so one whose living twin exists wins\n // the staging rather than colliding with it. That case is already an error\n // the gate refuses before any merge — `usecase.flow-exists`,\n // `deployment.doc-exists` — and grading the feature's version is the more\n // useful of the two answers to give an author who is about to be told to\n // delete the file.\n const mine = new Set<string>();\n for (const document of req.documents) {\n await stage(root, document.rel, document.path, authored);\n mine.add(document.rel);\n }\n const doc = asLoadedDoc(await loadProject(root, await architectureDocuments(root)));\n // TWO SPELLINGS REACH HERE and both must map, which is the one thing this\n // helper cannot be allowed to get half right. A parse error's\n // `sourceFsPath` is an ABSOLUTE path into the temp tree; a view's\n // `sourcePath` is already the project-RELATIVE rel LikeC4 reports. The\n // rel-first lookup answers the second, and the relative() computation the\n // first. Handling only one leaves the other silently returning the temp\n // spelling, which is a finding naming a directory that no longer exists.\n const real = (path: string): string =>\n authored.get(spell(path)) ?? authored.get(spell(relative(root, path))) ?? path;\n if (doc.errors.length > 0) {\n // An error naming no document is LikeC4 declining to attribute one. It\n // still travels: a hole loam cannot place is still a hole, and dropping\n // it here would grade the feature clean on the strength of a missing\n // field.\n return {\n kind: \"unreadable\",\n errors: doc.errors.map((e) => (e.sourceFsPath === undefined ? e.message : `${real(e.sourceFsPath)}: ${e.message}`)),\n };\n }\n return { kind: \"read\", doc, real, mine };\n } catch (err) {\n // The `unreadable` arm is a PROMISE, not a description of the happy path.\n // Two things under here throw rather than return: `loadProject` refuses an\n // error it cannot attribute to a document, and every `mkdir`/`copyFile` in\n // the staging above can fail on a full disk or a denied temp directory.\n // Callers run inside `loam validate --feature` and the archive gate — and\n // the one behaviour a check added to those may never have is failing the\n // run it was added to. So a throw becomes the same could-not-look answer a\n // parse error already produces, which each caller turns into its own\n // refusal that writes nothing.\n return { kind: \"unreadable\", errors: [err instanceof Error ? err.message : String(err)] };\n } finally {\n await rm(root, { recursive: true, force: true });\n }\n}\n\n/** What the landscape looks like after this feature's C4 delta is spliced in. */\ntype MergedLandscape =\n /** The delta adds nothing, or there is none — the living landscape stands. */\n | { kind: \"living\" }\n /** The merge preview, as authored bytes. */\n | { kind: \"merged\"; content: string }\n /** The merge could not be computed; `planLandscape` owns the refusal and says why. */\n | { kind: \"failed\"; reason: string };\n\n/**\n * The merge preview, computed with the SAME function `loam archive` commits\n * with.\n *\n * Reusing `planLandscapeMerge` rather than approximating it is what makes this\n * an answer about the archive rather than a second opinion beside it: an\n * element placed differently, a tag stripped differently, or a splice refused\n * there and allowed here would be a document graded against a map the merge\n * never writes.\n *\n * THE PARITY IS THE MERGE, NOT THE WHOLE GATE, and the difference is worth\n * naming rather than leaving for a reader to discover. `planLandscape` runs one\n * check BEFORE it computes the merge and this does not: a delta declaring the\n * feature tag on a specification KIND is refused there, because every element\n * of that kind inherits the tag and the merge would splice the whole document.\n * Such a delta reaches `newEls` here unfiltered, so the preview this grades\n * against is one the archive would never write. It changes no verdict — that\n * archive refuses either way, before anything is written — and it is recorded\n * because the next reader to widen either side needs to know the two are not\n * the same gate.\n *\n * A throw is `failed` rather than propagated. Every `LandscapeSpliceError` this\n * can raise is one `planLandscape` raises again — with its own message, its own\n * refusal code, and before anything is written — so throwing here would only\n * decide which of two identical refusals an author reads first.\n */\nasync function mergedLandscape(req: StagedProjectRequest): Promise<MergedLandscape> {\n const deltaPath = featurePaths(req.featureDir).delta;\n const landscape = landscapePath(req.docsDir);\n if (!existsSync(deltaPath) || !existsSync(landscape)) return { kind: \"living\" };\n const delta = await (req.load ?? loadFile)(deltaPath);\n if (delta.errors.length > 0) {\n return {\n kind: \"failed\",\n reason: `delta.likec4 has ${delta.errors.length} parse error(s) — a feature's own documents cannot be graded against a map that does not parse`,\n };\n }\n try {\n const plan = await planLandscapeMerge({\n landscapeText: await text(landscape),\n deltaText: await text(deltaPath),\n deltaElements: delta.elements,\n newEls: delta.elements.filter((e) => e.tags.includes(req.featureId)),\n newRels: delta.relationships.filter((r) => r.tags.includes(req.featureId)),\n featureId: req.featureId,\n });\n return plan.content === null ? { kind: \"living\" } : { kind: \"merged\", content: plan.content };\n } catch (err) {\n return { kind: \"failed\", reason: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/** Read a document loam is about to hand LikeC4, refusing the encodings that decode as empty. */\nasync function text(path: string): Promise<string> {\n return decodeDocument(await readFile(path), path);\n}\n\n/** A path spelled the way a staged rel is spelled — `/`-separated, on every platform. */\nfunction spell(path: string): string {\n return path.split(/[\\\\/]/).join(\"/\");\n}\n\n/** Copy one authored document into the staged tree, remembering where it came from. */\nasync function stage(root: string, rel: string, path: string, authored: Map<string, string>): Promise<void> {\n const dest = join(root, ...rel.split(\"/\"));\n await mkdir(dirname(dest), { recursive: true });\n await copyFile(path, dest);\n authored.set(rel, resolve(path));\n}\n\n/** Write one document into the staged tree from bytes loam holds rather than a file. */\nasync function write(root: string, rel: string, content: string): Promise<void> {\n const dest = join(root, ...rel.split(\"/\"));\n await mkdir(dirname(dest), { recursive: true });\n await writeFile(dest, content, \"utf8\");\n}\n"]}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Which `services/<id>/` directory a modelled element belongs to.
3
+ *
4
+ * A distinct subject from loading a document, and a distinct phase: the
5
+ * loader beside this package (`../likec4.ts`) turns LikeC4's parse output into
6
+ * loam's neutral `Elem`/`Rel` records, and everything here runs AFTER that, over
7
+ * those records, answering one question the model itself never states outright.
8
+ * It is the join every fleet-wide check rests on — the landscape↔services
9
+ * reconciliation, the spine's edge grouping, `Covers:` resolution, health.yaml's
10
+ * dependency matching — which is why `docs/DESIGN.md`'s Open-decisions table
11
+ * audits its call sites as a subject of their own rather than as a detail of the
12
+ * loader.
13
+ *
14
+ * The resolution order below is the whole of it, and its last rung is the one
15
+ * that lies: read that comment before adding a caller.
16
+ */
17
+ import { declaredService } from "../../kernel/ids/service.js";
18
+ import {} from "../likec4.js";
19
+ /**
20
+ * The service directory an element stands for: an explicit
21
+ * `metadata { service '<id>' }` binding wins, the title is the fallback.
22
+ *
23
+ * The fallback is what every docs repo written before the binding existed relies
24
+ * on, and it is also the trap the binding exists to close: matching on the title
25
+ * means renaming a box in a diagram silently unlinks it from its service, and
26
+ * every check that joined the two just stops finding anything.
27
+ */
28
+ export function elementService(e) {
29
+ return e.service ?? declaredService(e.title);
30
+ }
31
+ /**
32
+ * Every id an endpoint may be filed under, nearest first: `a.b.c`, `a.b`, `a`.
33
+ *
34
+ * A landscape does not have to draw a service as one opaque box. The moment
35
+ * somebody models its containers — `paymentService.api`, `paymentService.worker`
36
+ * — an edge drawn INTO a container is still an edge into the service, and every
37
+ * join that groups by service has to know that. Resolving only the exact id is
38
+ * what made those edges invisible: the spine check silently skipped them, and
39
+ * the no-openapi grace treated a service with a dozen inbound container calls as
40
+ * one nobody calls at all.
41
+ */
42
+ function ancestorIds(id) {
43
+ const out = [id];
44
+ for (let dot = id.lastIndexOf("."); dot !== -1; dot = id.lastIndexOf(".", dot - 1)) {
45
+ out.push(id.slice(0, dot));
46
+ }
47
+ return out;
48
+ }
49
+ /**
50
+ * A memoized `id -> service` resolver over one document.
51
+ *
52
+ * Resolution order, and why it is this order:
53
+ *
54
+ * 1. the nearest ancestor (the id itself first) carrying an explicit
55
+ * `metadata { service '<id>' }` — a binding is a claim somebody wrote down,
56
+ * so it outranks every guess;
57
+ * 2. the nearest ancestor whose title names a REAL `services/<id>/` directory —
58
+ * positive evidence from the filesystem, available only to callers that
59
+ * hand in `known`;
60
+ * 3. the element's own title, and finally the raw id — today's fallback, kept
61
+ * LAST because it is the one that lies: it happily resolves
62
+ * `paymentService.api` to "api", a service that has never existed.
63
+ *
64
+ * Built once per document and shared, because it is called inside loops over
65
+ * every relationship and the walk up the ancestor chain is not free.
66
+ */
67
+ export function serviceResolver(elements, known) {
68
+ const byId = new Map(elements.map((e) => [e.id, e]));
69
+ const memo = new Map();
70
+ return (id) => {
71
+ const hit = memo.get(id);
72
+ if (hit !== undefined)
73
+ return hit;
74
+ let answer;
75
+ for (const candidate of ancestorIds(id)) {
76
+ const e = byId.get(candidate);
77
+ if (e?.service !== undefined) {
78
+ answer = e.service;
79
+ break;
80
+ }
81
+ }
82
+ if (answer === undefined && known !== undefined) {
83
+ for (const candidate of ancestorIds(id)) {
84
+ const e = byId.get(candidate);
85
+ if (e !== undefined && known.has(e.title)) {
86
+ answer = declaredService(e.title);
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ if (answer === undefined) {
92
+ const self = byId.get(id);
93
+ answer = self ? elementService(self) : declaredService(id);
94
+ }
95
+ memo.set(id, answer);
96
+ return answer;
97
+ };
98
+ }
99
+ /**
100
+ * The service a relationship endpoint belongs to. An id that names no element
101
+ * resolves to itself, so a partial document degrades to the id rather than
102
+ * throwing.
103
+ *
104
+ * `known` is the set of service directories that actually exist; pass it
105
+ * wherever the caller has enumerated `services/`, so an edge into a modelled
106
+ * container resolves to the service that owns it instead of to the container's
107
+ * own title. Callers with a single document and no repository in hand omit it
108
+ * and keep the pre-existing behaviour.
109
+ */
110
+ export function serviceOf(elements, id, known) {
111
+ return serviceResolver(elements, known)(id);
112
+ }
113
+ /**
114
+ * One `id -> service` resolver per document, not one per lookup.
115
+ *
116
+ * `serviceOf` builds a fresh id map on every call, and this axis calls it twice
117
+ * for every relationship in scope — inside loops that already run over every
118
+ * relationship a service can see. `serviceResolver`'s own doc says it is "built
119
+ * once per document and shared"; this is where both axes that need it keep
120
+ * that promise — `../arch.ts`'s `coversEdge` and `./steps.ts`'s `resolvedTier`,
121
+ * each exported and each handed only the element array.
122
+ *
123
+ * A module-level cache is normally the wrong shape here, because a command runs
124
+ * against whatever directory it was invoked in. This one is safe: the key is the
125
+ * per-invocation `Elem[]` a parse produced, its value is a pure function of that
126
+ * key alone, and nothing in it is derived from the working directory — so two
127
+ * runs can never see each other's answer, and the entry dies with the document.
128
+ * Nothing mutates an `Elem[]` after parse.
129
+ *
130
+ * Two tiers because the fleet set is part of the answer: the same document
131
+ * resolves differently with and without `known`, so a cache keyed on the
132
+ * elements alone handed a with-fleet caller the without-fleet resolver.
133
+ * Callers build ONE set per run and pass the same instance through, which is
134
+ * what lets the inner WeakMap key on the set's identity.
135
+ */
136
+ const RESOLVERS = new WeakMap();
137
+ const FLEET_RESOLVERS = new WeakMap();
138
+ export function resolverFor(elements, known) {
139
+ if (known === undefined) {
140
+ const cached = RESOLVERS.get(elements);
141
+ if (cached !== undefined)
142
+ return cached;
143
+ const resolver = serviceResolver(elements);
144
+ RESOLVERS.set(elements, resolver);
145
+ return resolver;
146
+ }
147
+ let perSet = FLEET_RESOLVERS.get(elements);
148
+ if (perSet === undefined) {
149
+ perSet = new WeakMap();
150
+ FLEET_RESOLVERS.set(elements, perSet);
151
+ }
152
+ const cached = perSet.get(known);
153
+ if (cached !== undefined)
154
+ return cached;
155
+ const resolver = serviceResolver(elements, known);
156
+ perSet.set(known, resolver);
157
+ return resolver;
158
+ }
159
+ //# sourceMappingURL=service.js.map