@ybotok/loam 0.2.0-alpha.1 → 0.2.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -1,105 +1,40 @@
1
1
  /**
2
- * The event axis's fleet questions, each read lazily — ../lookups.ts's
3
- * pattern on the message join.
2
+ * The event axis's fleet question, read lazily — ../lookups.ts's pattern on
3
+ * the message join.
4
4
  *
5
- * Both reach outside the delta: the consumer scan is a landscape spin-up
6
- * plus a walk of every other service's living requirements, and the
7
- * in-flight scan reads every active feature's asyncapi delta. Closures over
8
- * a cache rather than arguments computed up front, so each is paid only
9
- * when its question is first asked — but the two prices differ, and the
10
- * difference is deliberate: the consumer scan is paid only by a feature
11
- * that actually retires production, while the in-flight scan is paid by ANY
12
- * feature that declares a message at all, because
5
+ * It reaches outside the delta: the in-flight scan reads every active
6
+ * feature's asyncapi delta. A closure over a cache rather than an argument
7
+ * computed up front, so it is paid only when its question is first asked —
8
+ * and it is paid by ANY feature that declares a message at all, because
13
9
  * `asyncapi.message-conflict`'s question ("does an in-flight feature define
14
10
  * this elsewhere?") is asked of every healthy declaration — unlike the
15
11
  * OpenAPI mirror, whose `definedElsewhere` is reached only inside a breach
16
12
  * branch. That standing cost matters most on `archive`'s gate, which runs
17
13
  * without a FleetContext, so each read here is a real parse.
14
+ *
15
+ * The consumer scan that used to sit beside it — who still consumes a message
16
+ * a service retires — is the consumer index's now (`core/consumers/`), paid
17
+ * only by a feature that actually retires production: `loam diff` asks the
18
+ * same question, and the two copies had drifted apart in which documents they
19
+ * read.
18
20
  */
19
- import { existsSync } from "node:fs";
20
- import { readFile } from "node:fs/promises";
21
- import { loadFile } from "../../c4/likec4.js";
22
- import { serviceResolver } from "../../c4/resolve/service.js";
23
- import {} from "../../kernel/ids/service.js";
24
- import { featureSpecPaths, landscapePath, SPEC_AXES } from "../../repo/paths.js";
25
- import { enumeratedServiceIds, locateServicePaths } from "../../repo/service-target.js";
21
+ import { featureSpecPaths } from "../../repo/paths.js";
26
22
  import { listFeatures } from "../../repo/repo.js";
27
23
  import { readAsyncapi } from "../../asyncapi/read.js";
28
- import { parseRequirements } from "../../document/parse.js";
29
- import {} from "../../document/spec.js";
30
24
  export function eventLookups(scope, context) {
31
25
  const { docsDir, featureId } = scope;
32
- // The enumerated fleet, read once and shared by both halves of the
33
- // consumer scan — ../lookups.ts's discipline, verbatim.
34
- let serviceIds;
35
- const enumeratedServices = async () => (serviceIds ??= await enumeratedServiceIds(docsDir, context));
36
- // Living requirements per service, BOTH axes: `Publishes:`/`Consumes:`
37
- // live in arch.spec.md at least as often as in spec.md (the outbox
38
- // requirement), so a scan of spec.md alone would answer "nobody consumes
39
- // it" over the file most authors use.
40
- const livingReqs = new Map();
41
- const livingRequirements = async (service) => {
42
- let reqs = livingReqs.get(service);
43
- if (reqs === undefined) {
44
- reqs = [];
45
- for (const axis of SPEC_AXES) {
46
- const p = (await locateServicePaths(docsDir, service, context))[axis.key];
47
- if (!existsSync(p))
48
- continue;
49
- reqs.push(...(context === undefined
50
- ? parseRequirements(await readFile(p, "utf8"))
51
- : await context.readRequirements(p)));
52
- }
53
- livingReqs.set(service, reqs);
54
- }
55
- return reqs;
56
- };
57
- // The landscape half: a `consumes` edge is a consumer somebody drew. The
58
- // message join is fleet-global (names are not service-scoped), so every
59
- // edge naming the message counts, whoever its producer is — resolved
60
- // through the enumerated fleet so a landscape that models CONTAINERS
61
- // stays visible, ../lookups.ts's edgeConsumers lesson.
62
- // (`core/diff/victims.ts` mirrors this scan over an already-read fleet —
63
- // change the rules or the victim strings here and that module owes the
64
- // same change; a third copy forces the shared extraction, rule 13.)
65
- let livingLandscape;
66
- const messageConsumers = async (service, message) => {
67
- if (livingLandscape === undefined) {
68
- const path = landscapePath(docsDir);
69
- livingLandscape = existsSync(path)
70
- ? context === undefined
71
- ? await loadFile(path)
72
- : await context.loadLikeC4(path)
73
- : null;
74
- }
75
- const out = [];
76
- // An unreadable landscape proves nothing either way; `landscape.invalid`
77
- // is validate's finding to make.
78
- if (livingLandscape !== null && livingLandscape.errors.length === 0) {
79
- const resolve = serviceResolver(livingLandscape.elements, new Set(await enumeratedServices()));
80
- for (const r of livingLandscape.relationships) {
81
- if (r.consumes !== message || resolve(r.target) === service)
82
- continue;
83
- out.push(`edge ${resolve(r.source)} → ${resolve(r.target)}${r.title === undefined ? "" : ` ("${r.title}")`}`);
84
- }
85
- }
86
- for (const other of (await enumeratedServices()).filter((id) => id !== service)) {
87
- for (const r of await livingRequirements(other)) {
88
- if (r.consumes.includes(message))
89
- out.push(`${other}'s living requirement '${r.name}'`);
90
- }
91
- }
92
- return out;
93
- };
94
26
  // What OTHER features in flight declare, per (service, message) — the
95
27
  // asyncapi mirror of pending.ts's activeOpAdditions, living HERE because
96
28
  // it shares this module's laziness and its consumers. Removal markers do
97
29
  // not count: a feature retiring a message is not adding it. First feature
98
30
  // wins a clash — one name to archive first is enough to make progress.
31
+ // The senders ride the same walk: both questions read the same deltas, and
32
+ // a second walk is a second parse of every one of them on the archive gate.
99
33
  let inFlight = null;
100
- const messageDefinedElsewhere = async (service, message) => {
34
+ const scan = async () => {
101
35
  if (inFlight === null) {
102
- inFlight = new Map();
36
+ const declared = new Map();
37
+ const sent = new Map();
103
38
  for (const feature of await listFeatures(docsDir, {}, context)) {
104
39
  if (feature.id === featureId)
105
40
  continue;
@@ -109,14 +44,20 @@ export function eventLookups(scope, context) {
109
44
  if (m.remove === true)
110
45
  continue;
111
46
  const k = `${svc} ${m.name}`;
112
- if (!inFlight.has(k))
113
- inFlight.set(k, feature.id);
47
+ if (!declared.has(k))
48
+ declared.set(k, feature.id);
114
49
  }
50
+ for (const name of doc.sent)
51
+ if (!sent.has(name))
52
+ sent.set(name, feature.id);
115
53
  }
116
54
  }
55
+ inFlight = { declared, sent };
117
56
  }
118
- return inFlight.get(`${service} ${message}`);
57
+ return inFlight;
119
58
  };
120
- return { messageConsumers, messageDefinedElsewhere };
59
+ const messageDefinedElsewhere = async (service, message) => (await scan()).declared.get(`${service} ${message}`);
60
+ const messageSentElsewhere = async (message) => (await scan()).sent.get(message);
61
+ return { messageDefinedElsewhere, messageSentElsewhere };
121
62
  }
122
63
  //# sourceMappingURL=lookups.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lookups.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/lookups.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The event axis's fleet questions, each read lazily — ../lookups.ts's\n * pattern on the message join.\n *\n * Both reach outside the delta: the consumer scan is a landscape spin-up\n * plus a walk of every other service's living requirements, and the\n * in-flight scan reads every active feature's asyncapi delta. Closures over\n * a cache rather than arguments computed up front, so each is paid only\n * when its question is first asked — but the two prices differ, and the\n * difference is deliberate: the consumer scan is paid only by a feature\n * that actually retires production, while the in-flight scan is paid by ANY\n * feature that declares a message at all, because\n * `asyncapi.message-conflict`'s question (\"does an in-flight feature define\n * this elsewhere?\") is asked of every healthy declaration — unlike the\n * OpenAPI mirror, whose `definedElsewhere` is reached only inside a breach\n * branch. That standing cost matters most on `archive`'s gate, which runs\n * without a FleetContext, so each read here is a real parse.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { loadFile, type LoadedDoc } from \"../../c4/likec4.js\";\nimport { serviceResolver } from \"../../c4/resolve/service.js\";\nimport { type PathableService } from \"../../kernel/ids/service.js\";\nimport { featureSpecPaths, landscapePath, SPEC_AXES } from \"../../repo/paths.js\";\nimport { enumeratedServiceIds, locateServicePaths } from \"../../repo/service-target.js\";\nimport { listFeatures } from \"../../repo/repo.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport { parseRequirements } from \"../../document/parse.js\";\nimport { type Requirement } from \"../../document/spec.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/** What the event checks may ask about the world outside this feature's own files. */\nexport interface EventLookups {\n /**\n * Living consumers of one message a `service` is retiring: landscape\n * `consumes` edges, and other services' living requirements' `Consumes:`\n * lines (spec.md and arch.spec.md both — the outbox file is the canonical\n * home of an event line). The retiring service itself is excluded from\n * both: its own consumption leaves in the same feature, and counting it\n * would gate every self-consuming producer on its own retirement.\n */\n messageConsumers(service: string, message: string): Promise<string[]>;\n /** The in-flight feature (if any) whose asyncapi delta adds or edits (service, message). */\n messageDefinedElsewhere(service: string, message: string): Promise<string | undefined>;\n}\n\n/** The identity of the feature being graded — what the lazy scans exclude. */\nexport interface EventScope {\n docsDir: DocsDir;\n featureId: string;\n}\n\nexport function eventLookups(scope: EventScope, context?: FleetContext): EventLookups {\n const { docsDir, featureId } = scope;\n\n // The enumerated fleet, read once and shared by both halves of the\n // consumer scan — ../lookups.ts's discipline, verbatim.\n let serviceIds: PathableService[] | undefined;\n const enumeratedServices = async (): Promise<PathableService[]> =>\n (serviceIds ??= await enumeratedServiceIds(docsDir, context));\n\n // Living requirements per service, BOTH axes: `Publishes:`/`Consumes:`\n // live in arch.spec.md at least as often as in spec.md (the outbox\n // requirement), so a scan of spec.md alone would answer \"nobody consumes\n // it\" over the file most authors use.\n const livingReqs = new Map<string, Requirement[]>();\n const livingRequirements = async (service: PathableService): Promise<Requirement[]> => {\n let reqs = livingReqs.get(service);\n if (reqs === undefined) {\n reqs = [];\n for (const axis of SPEC_AXES) {\n const p = (await locateServicePaths(docsDir, service, context))[axis.key];\n if (!existsSync(p)) continue;\n reqs.push(\n ...(context === undefined\n ? parseRequirements(await readFile(p, \"utf8\"))\n : await context.readRequirements(p)),\n );\n }\n livingReqs.set(service, reqs);\n }\n return reqs;\n };\n\n // The landscape half: a `consumes` edge is a consumer somebody drew. The\n // message join is fleet-global (names are not service-scoped), so every\n // edge naming the message counts, whoever its producer is — resolved\n // through the enumerated fleet so a landscape that models CONTAINERS\n // stays visible, ../lookups.ts's edgeConsumers lesson.\n // (`core/diff/victims.ts` mirrors this scan over an already-read fleet —\n // change the rules or the victim strings here and that module owes the\n // same change; a third copy forces the shared extraction, rule 13.)\n let livingLandscape: LoadedDoc | null | undefined;\n const messageConsumers = async (service: string, message: string): Promise<string[]> => {\n if (livingLandscape === undefined) {\n const path = landscapePath(docsDir);\n livingLandscape = existsSync(path)\n ? context === undefined\n ? await loadFile(path)\n : await context.loadLikeC4(path)\n : null;\n }\n const out: string[] = [];\n // An unreadable landscape proves nothing either way; `landscape.invalid`\n // is validate's finding to make.\n if (livingLandscape !== null && livingLandscape.errors.length === 0) {\n const resolve = serviceResolver(livingLandscape.elements, new Set(await enumeratedServices()));\n for (const r of livingLandscape.relationships) {\n if (r.consumes !== message || resolve(r.target) === service) continue;\n out.push(`edge ${resolve(r.source)} → ${resolve(r.target)}${r.title === undefined ? \"\" : ` (\"${r.title}\")`}`);\n }\n }\n for (const other of (await enumeratedServices()).filter((id) => id !== service)) {\n for (const r of await livingRequirements(other)) {\n if (r.consumes.includes(message)) out.push(`${other}'s living requirement '${r.name}'`);\n }\n }\n return out;\n };\n\n // What OTHER features in flight declare, per (service, message) — the\n // asyncapi mirror of pending.ts's activeOpAdditions, living HERE because\n // it shares this module's laziness and its consumers. Removal markers do\n // not count: a feature retiring a message is not adding it. First feature\n // wins a clash — one name to archive first is enough to make progress.\n let inFlight: Map<string, string> | null = null;\n const messageDefinedElsewhere = async (service: string, message: string): Promise<string | undefined> => {\n if (inFlight === null) {\n inFlight = new Map<string, string>();\n for (const feature of await listFeatures(docsDir, {}, context)) {\n if (feature.id === featureId) continue;\n for (const svc of feature.services) {\n const doc = await readAsyncapi(featureSpecPaths(feature.dir, svc).asyncapi, context);\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const k = `${svc} ${m.name}`;\n if (!inFlight.has(k)) inFlight.set(k, feature.id);\n }\n }\n }\n }\n return inFlight.get(`${service} ${message}`);\n };\n\n return { messageConsumers, messageDefinedElsewhere };\n}\n"]}
1
+ {"version":3,"file":"lookups.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/lookups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAyBtD,MAAM,UAAU,YAAY,CAAC,KAAiB,EAAE,OAAsB;IACpE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAErC,sEAAsE;IACtE,yEAAyE;IACzE,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAI,QAAQ,GAAwE,IAAI,CAAC;IACzF,MAAM,IAAI,GAAG,KAAK,IAA2E,EAAE;QAC7F,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;YAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;YACvC,KAAK,MAAM,OAAO,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC/D,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS;oBAAE,SAAS;gBACvC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACnC,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;oBACrF,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;wBAC7B,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI;4BAAE,SAAS;wBAChC,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;wBAC7B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;4BAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;oBACpD,CAAC;oBACD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,IAAI;wBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;4BAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC/E,CAAC;YACH,CAAC;YACD,QAAQ,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAChC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,uBAAuB,GAAG,KAAK,EAAE,OAAe,EAAE,OAAe,EAA+B,EAAE,CACtG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC,CAAC;IACvD,MAAM,oBAAoB,GAAG,KAAK,EAAE,OAAe,EAA+B,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAEtH,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["/**\n * The event axis's fleet question, read lazily — ../lookups.ts's pattern on\n * the message join.\n *\n * It reaches outside the delta: the in-flight scan reads every active\n * feature's asyncapi delta. A closure over a cache rather than an argument\n * computed up front, so it is paid only when its question is first asked —\n * and it is paid by ANY feature that declares a message at all, because\n * `asyncapi.message-conflict`'s question (\"does an in-flight feature define\n * this elsewhere?\") is asked of every healthy declaration — unlike the\n * OpenAPI mirror, whose `definedElsewhere` is reached only inside a breach\n * branch. That standing cost matters most on `archive`'s gate, which runs\n * without a FleetContext, so each read here is a real parse.\n *\n * The consumer scan that used to sit beside it — who still consumes a message\n * a service retires — is the consumer index's now (`core/consumers/`), paid\n * only by a feature that actually retires production: `loam diff` asks the\n * same question, and the two copies had drifted apart in which documents they\n * read.\n */\nimport { featureSpecPaths } from \"../../repo/paths.js\";\nimport { listFeatures } from \"../../repo/repo.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/** What the event checks may ask about the world outside this feature's own files. */\nexport interface EventLookups {\n /** The in-flight feature (if any) whose asyncapi delta adds or edits (service, message). */\n messageDefinedElsewhere(service: string, message: string): Promise<string | undefined>;\n /**\n * The in-flight feature (if any) whose asyncapi delta declares an `action:\n * send` for `message`, on any service — the producer a consumer this feature\n * adds would be waiting for (`./produced.ts`). Senders only, for\n * `ConsumerIndex.producers`' reason: every consumer declares the message too,\n * with `action: receive`, and counting that named the consumer its own\n * producer.\n */\n messageSentElsewhere(message: string): Promise<string | undefined>;\n}\n\n/** The identity of the feature being graded — what the lazy scans exclude. */\nexport interface EventScope {\n docsDir: DocsDir;\n featureId: string;\n}\n\nexport function eventLookups(scope: EventScope, context?: FleetContext): EventLookups {\n const { docsDir, featureId } = scope;\n\n // What OTHER features in flight declare, per (service, message) — the\n // asyncapi mirror of pending.ts's activeOpAdditions, living HERE because\n // it shares this module's laziness and its consumers. Removal markers do\n // not count: a feature retiring a message is not adding it. First feature\n // wins a clash — one name to archive first is enough to make progress.\n // The senders ride the same walk: both questions read the same deltas, and\n // a second walk is a second parse of every one of them on the archive gate.\n let inFlight: { declared: Map<string, string>; sent: Map<string, string> } | null = null;\n const scan = async (): Promise<{ declared: Map<string, string>; sent: Map<string, string> }> => {\n if (inFlight === null) {\n const declared = new Map<string, string>();\n const sent = new Map<string, string>();\n for (const feature of await listFeatures(docsDir, {}, context)) {\n if (feature.id === featureId) continue;\n for (const svc of feature.services) {\n const doc = await readAsyncapi(featureSpecPaths(feature.dir, svc).asyncapi, context);\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const k = `${svc} ${m.name}`;\n if (!declared.has(k)) declared.set(k, feature.id);\n }\n for (const name of doc.sent) if (!sent.has(name)) sent.set(name, feature.id);\n }\n }\n inFlight = { declared, sent };\n }\n return inFlight;\n };\n const messageDefinedElsewhere = async (service: string, message: string): Promise<string | undefined> =>\n (await scan()).declared.get(`${service} ${message}`);\n const messageSentElsewhere = async (message: string): Promise<string | undefined> => (await scan()).sent.get(message);\n\n return { messageDefinedElsewhere, messageSentElsewhere };\n}\n"]}
@@ -0,0 +1,100 @@
1
+ import { stageMergedProject } from "../../c4/project/staged.js";
2
+ import { externalProducerOf } from "../../c4/resolve/external.js";
3
+ import { serviceResolver } from "../../c4/resolve/service.js";
4
+ import { extendingModels } from "../../c4/service-model/fleet/extending.js";
5
+ import { enumeratedServiceIds } from "../../repo/service-target.js";
6
+ import { producersByMessage } from "../../asyncapi/producers.js";
7
+ /**
8
+ * The fleet services that would send `message` after this merge, sorted —
9
+ * positive evidence only. A service this feature carries an event delta for
10
+ * (or whose delta does not read) sends what the simulated merge leaves it and
11
+ * nothing else; every other service sends what its living contract does. Read
12
+ * alone, the living half would let a feature retiring a message from both of
13
+ * its senders pass on each one's living contract.
14
+ */
15
+ export async function fleetSendersAfter(declared, consumers, message) {
16
+ const touched = new Set([...declared.featureDocs.keys(), ...declared.unreadable]);
17
+ const living = (await consumers.producers(message)).filter((id) => !touched.has(id));
18
+ const merged = [...declared.sentAfter].filter(([, sent]) => sent.includes(message)).map(([id]) => id);
19
+ return [...new Set([...living, ...merged])].sort();
20
+ }
21
+ export async function unproducedIssues(req) {
22
+ const { scope, declared, consumers, lookups, context } = req;
23
+ if (req.claims.length === 0)
24
+ return [];
25
+ // One finding per (consumer, message), however many lines and edges claim
26
+ // it — the grain service scope reports at, so the two counts agree.
27
+ const groups = new Map();
28
+ for (const claim of req.claims) {
29
+ const key = JSON.stringify([claim.service, claim.message]);
30
+ groups.set(key, [...(groups.get(key) ?? []), claim]);
31
+ }
32
+ // The post-merge map, staged at most once and only when the living answers
33
+ // ran out; null when it cannot be had, which suspends rather than convicts.
34
+ let staged;
35
+ const mergedMap = () => (staged ??= (async () => {
36
+ const project = await stageMergedProject({
37
+ ...scope,
38
+ documents: [],
39
+ ...(context === undefined ? {} : { load: (path) => context.loadLikeC4(path) }),
40
+ models: await extendingModels(scope.docsDir, context),
41
+ });
42
+ return project.kind === "read" ? project.doc : null;
43
+ })());
44
+ /** true = sent; false = nothing sends it; undefined = loam could not look. */
45
+ const produced = async (message) => {
46
+ if ((await fleetSendersAfter(declared, consumers, message)).length > 0)
47
+ return true;
48
+ if ((await consumers.externalProducer(message)) !== null)
49
+ return true;
50
+ const map = await mergedMap();
51
+ if (map === null)
52
+ return undefined;
53
+ return externalProducerOf(message, map, serviceResolver(map.elements, req.known), req.known) !== null;
54
+ };
55
+ // Read only once some consumer has no producer, which a healthy feature
56
+ // never reaches: the fleet's contracts are already behind the consumer
57
+ // index's memo, but this read is a plain one (it is the walk service scope
58
+ // suspends on, and must be the same one).
59
+ let suspended;
60
+ const doubt = () => (suspended ??= (async () => declared.unreadable.size > 0 ||
61
+ (await producersByMessage(scope.docsDir, await enumeratedServiceIds(scope.docsDir, context), context)).unreadable.length >
62
+ 0)());
63
+ const issues = [];
64
+ for (const [, claims] of [...groups].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
65
+ const [first] = claims;
66
+ if (first === undefined)
67
+ continue;
68
+ const { service, message } = first;
69
+ const sent = await produced(message);
70
+ if (sent !== false)
71
+ continue;
72
+ if (await doubt())
73
+ continue;
74
+ const where = claims.map((c) => c.via).join(", ");
75
+ const pending = await lookups.messageSentElsewhere(message);
76
+ if (pending !== undefined) {
77
+ issues.push({
78
+ severity: "warn",
79
+ code: claims.some((c) => c.source === "requirement") ? "spec-event.message-pending" : "c4-event.message-pending",
80
+ subject: service,
81
+ message: `${service}: ${where} consumes '${message}', whose only sender is in-flight ${pending} — archive it first: ` +
82
+ `archived before it, this feature leaves '${message}' with no producer and the next \`loam validate --all\` ` +
83
+ `reports spine.message-unproduced against ${service}`,
84
+ });
85
+ continue;
86
+ }
87
+ issues.push({
88
+ severity: "error",
89
+ code: "spine.message-unproduced",
90
+ subject: service,
91
+ message: `${service}: ${where} consumes '${message}', but after this merge no service in the fleet and no #external ` +
92
+ `element would declare it is sent — the message would have no producer anywhere, and the next ` +
93
+ `\`loam validate --all\` reports this same code against ${service}. Add the producer's send (an action: send ` +
94
+ `operation in its asyncapi.yaml, living or in this feature's specs/<service>/ delta), draw the #external ` +
95
+ `system publishing it (a publishes edge from an #external element), or drop the consumption from this feature`,
96
+ });
97
+ }
98
+ return issues;
99
+ }
100
+ //# sourceMappingURL=produced.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"produced.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/produced.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Does anything still SEND what this feature makes a service consume?\n *\n * `spine.message-unproduced` asked at feature scope — the question validate's\n * service target asks of every living consumer\n * (`commands/validate/service/events/message-producers.ts`), asked here of the\n * consumers this feature's merge would ADD. Until it was, a feature whose\n * delta newly `Consumes:` (or draws a `consumes` edge into) a message no\n * service and no #external element sends passed the feature gate, archived at\n * exit 0 under \"living spec + landscape are now complete + current.\", and the\n * very next `validate --all` reported the error against the consumer\n * (the real-fleet review, 2026-10-21). `./events.ts` had already computed who\n * sends a message after the merge — for retirements only.\n *\n * THE FLEET'S SENDERS ARE THE RETIREMENT GATE'S, read after the merge\n * (`fleetSendersAfter` below, which `./events.ts` calls too): a living\n * service's contract, except that a service this feature carries an event\n * delta for answers with what the merge leaves it (`declared.sentAfter`) and\n * nothing else. The consumer itself counts, as it does at service scope — a\n * relay that reads and writes one name produces it.\n *\n * THE OUTSIDE PRODUCER IS ASKED OF THE MAP THE MERGE LEAVES, and here the two\n * gates part on purpose. The retirement gate reads only the living map and\n * errs toward refusing a retirement (`./events.ts` says why). A consumer is the\n * opposite shape: its question is whether `validate --all` will find a\n * producer once the archive lands, and the landscape merge only ADDS — a\n * tagged `publishes` edge from an #external system is on that map. Reading\n * the delta alone was not enough, because a delta re-declares a living system\n * as an untagged stub (`salesforce = softwareSystem 'salesforce'`): the edge\n * lands, the living element keeps its #external tag, and the fleet is clean —\n * while a delta-only read saw no tag and refused. So the living map is asked\n * first (memoised, cheap), and only then the staged post-merge map\n * (`core/c4/project/staged.ts`, the archive's own merge), parsed once per\n * feature and only on the path where nothing else sends the message.\n *\n * THE SAME SUSPENSION. \"Nobody sends this\" is an argument from absence, and\n * service scope makes it only over a fleet whose every contract reads; one\n * unreadable `asyncapi.yaml` anywhere suspends it, because that file may be the\n * producer. A feature delta that does not read suspends it too — its own\n * `asyncapi.invalid` already gates — and so does a post-merge map that cannot\n * be staged: loam could not look, which is never \"nobody publishes it\".\n *\n * SOFTENED for a producer still in flight, on the `-pending` precedent every\n * other cross-feature join here follows: when another active feature's delta\n * declares the send, the message is \"introduced by another feature still in\n * flight\" — `spec-event.message-pending` for a requirement line, and\n * `c4-event.message-pending` for an edge. A warning, so two features meant to\n * ship together stay green while both are open, and its words say what the\n * wrong order costs.\n */\nimport type { LoadedDoc } from \"../../c4/likec4.js\";\nimport { stageMergedProject } from \"../../c4/project/staged.js\";\nimport { externalProducerOf } from \"../../c4/resolve/external.js\";\nimport { serviceResolver } from \"../../c4/resolve/service.js\";\nimport { extendingModels } from \"../../c4/service-model/fleet/extending.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport type { PathableService, RawServiceId } from \"../../kernel/ids/service.js\";\nimport { enumeratedServiceIds } from \"../../repo/service-target.js\";\nimport { producersByMessage } from \"../../asyncapi/producers.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { ConsumerIndex } from \"../../consumers/consumers.js\";\nimport type { DeclaredEvents, EventDeltaScope } from \"./declared.js\";\nimport type { EventLookups } from \"./lookups.js\";\n\n/** One place this feature's delta makes a service consume a message. */\nexport interface ConsumeClaim {\n service: PathableService;\n message: string;\n /** Which document says so — a requirement's `Consumes:` line, or a tagged `consumes` edge. */\n source: \"requirement\" | \"edge\";\n /** How a reader finds it: `requirement 'X'`, or `edge a → b`. */\n via: string;\n}\n\n/**\n * The fleet services that would send `message` after this merge, sorted —\n * positive evidence only. A service this feature carries an event delta for\n * (or whose delta does not read) sends what the simulated merge leaves it and\n * nothing else; every other service sends what its living contract does. Read\n * alone, the living half would let a feature retiring a message from both of\n * its senders pass on each one's living contract.\n */\nexport async function fleetSendersAfter(declared: DeclaredEvents, consumers: ConsumerIndex, message: string): Promise<string[]> {\n const touched = new Set<string>([...declared.featureDocs.keys(), ...declared.unreadable]);\n const living = (await consumers.producers(message)).filter((id) => !touched.has(id));\n const merged = [...declared.sentAfter].filter(([, sent]) => sent.includes(message)).map(([id]) => id);\n return [...new Set([...living, ...merged])].sort();\n}\n\n/** Everything the producer question reads, already computed by `./events.ts`. */\nexport interface ProducedRequest {\n scope: EventDeltaScope;\n declared: DeclaredEvents;\n consumers: ConsumerIndex;\n lookups: EventLookups;\n claims: readonly ConsumeClaim[];\n /** The services the merge leaves a directory for — the enumerated fleet plus this feature's `specs/`. */\n known: ReadonlySet<RawServiceId>;\n context?: FleetContext;\n}\n\nexport async function unproducedIssues(req: ProducedRequest): Promise<Issue[]> {\n const { scope, declared, consumers, lookups, context } = req;\n if (req.claims.length === 0) return [];\n // One finding per (consumer, message), however many lines and edges claim\n // it — the grain service scope reports at, so the two counts agree.\n const groups = new Map<string, ConsumeClaim[]>();\n for (const claim of req.claims) {\n const key = JSON.stringify([claim.service, claim.message]);\n groups.set(key, [...(groups.get(key) ?? []), claim]);\n }\n // The post-merge map, staged at most once and only when the living answers\n // ran out; null when it cannot be had, which suspends rather than convicts.\n let staged: Promise<LoadedDoc | null> | undefined;\n const mergedMap = (): Promise<LoadedDoc | null> =>\n (staged ??= (async () => {\n const project = await stageMergedProject({\n ...scope,\n documents: [],\n ...(context === undefined ? {} : { load: (path: string) => context.loadLikeC4(path) }),\n models: await extendingModels(scope.docsDir, context),\n });\n return project.kind === \"read\" ? project.doc : null;\n })());\n /** true = sent; false = nothing sends it; undefined = loam could not look. */\n const produced = async (message: string): Promise<boolean | undefined> => {\n if ((await fleetSendersAfter(declared, consumers, message)).length > 0) return true;\n if ((await consumers.externalProducer(message)) !== null) return true;\n const map = await mergedMap();\n if (map === null) return undefined;\n return externalProducerOf(message, map, serviceResolver(map.elements, req.known), req.known) !== null;\n };\n // Read only once some consumer has no producer, which a healthy feature\n // never reaches: the fleet's contracts are already behind the consumer\n // index's memo, but this read is a plain one (it is the walk service scope\n // suspends on, and must be the same one).\n let suspended: Promise<boolean> | undefined;\n const doubt = (): Promise<boolean> =>\n (suspended ??= (async () =>\n declared.unreadable.size > 0 ||\n (await producersByMessage(scope.docsDir, await enumeratedServiceIds(scope.docsDir, context), context)).unreadable.length >\n 0)());\n\n const issues: Issue[] = [];\n for (const [, claims] of [...groups].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {\n const [first] = claims;\n if (first === undefined) continue;\n const { service, message } = first;\n const sent = await produced(message);\n if (sent !== false) continue;\n if (await doubt()) continue;\n const where = claims.map((c) => c.via).join(\", \");\n const pending = await lookups.messageSentElsewhere(message);\n if (pending !== undefined) {\n issues.push({\n severity: \"warn\",\n code: claims.some((c) => c.source === \"requirement\") ? \"spec-event.message-pending\" : \"c4-event.message-pending\",\n subject: service,\n message:\n `${service}: ${where} consumes '${message}', whose only sender is in-flight ${pending} — archive it first: ` +\n `archived before it, this feature leaves '${message}' with no producer and the next \\`loam validate --all\\` ` +\n `reports spine.message-unproduced against ${service}`,\n });\n continue;\n }\n issues.push({\n severity: \"error\",\n code: \"spine.message-unproduced\",\n subject: service,\n message:\n `${service}: ${where} consumes '${message}', but after this merge no service in the fleet and no #external ` +\n `element would declare it is sent — the message would have no producer anywhere, and the next ` +\n `\\`loam validate --all\\` reports this same code against ${service}. Add the producer's send (an action: send ` +\n `operation in its asyncapi.yaml, living or in this feature's specs/<service>/ delta), draw the #external ` +\n `system publishing it (a publishes edge from an #external element), or drop the consumption from this feature`,\n });\n }\n return issues;\n}\n"]}
@@ -2,25 +2,28 @@
2
2
  * The questions the coherence checks ask lazily, each behind a per-service or
3
3
  * per-repo cache.
4
4
  *
5
- * All but one reach outside the delta — into a living spec, the fleet map, or
6
- * another feature in flight — and those are expensive: the landscape is a full
7
- * LikeC4 workspace spin-up, and the cross-feature scan is a walk of every
8
- * active delta. The exception is `undeprecatedByFeature`, which reads the
9
- * FEATURE's own openapi delta: it lives here anyway because it is the second
10
- * half of the one question `deprecatedInLiving` opens — "is this op dying, and
11
- * is this feature the resurrection?" — and splitting the pair across modules
12
- * would leave each half's cache discipline unexplained by the other. A feature
13
- * whose checks never need a lookup must not pay for it, which is why these are
14
- * closures over a cache rather than arguments computed up front.
5
+ * All but one reach outside the delta — into a living spec, a living
6
+ * contract, or another feature in flight — and the cross-feature scan is a
7
+ * walk of every active delta. The exception is `undeprecatedByFeature`, which
8
+ * reads the FEATURE's own openapi delta: it lives here anyway because it is the
9
+ * second half of the one question `deprecatedInLiving` opens — "is this op
10
+ * dying, and is this feature the resurrection?" — and splitting the pair across
11
+ * modules would leave each half's cache discipline unexplained by the other. A
12
+ * feature whose checks never need a lookup must not pay for it, which is why
13
+ * these are closures over a cache rather than arguments computed up front.
14
+ *
15
+ * The one question that used to live here and does not — who still CONSUMES an
16
+ * operation the feature takes out (`edgeConsumers`, `requirementConsumers`,
17
+ * `governorsAfterMerge`) — is the consumer index's now (`core/consumers/`),
18
+ * because `loam diff` and the archive planner ask it too and the three copies
19
+ * had drifted apart in which documents they read.
15
20
  */
16
21
  import { existsSync } from "node:fs";
17
22
  import { readFile } from "node:fs/promises";
18
- import { loadFile } from "../c4/likec4.js";
19
- import { serviceResolver } from "../c4/resolve/service.js";
20
23
  import {} from "../kernel/ids/service.js";
21
- import { featureSpecPaths, landscapePath } from "../repo/paths.js";
24
+ import { featureSpecPaths } from "../repo/paths.js";
22
25
  import { operations } from "../openapi/doc.js";
23
- import { enumeratedServiceIds, locateServicePaths } from "../repo/service-target.js";
26
+ import { locateServicePaths } from "../repo/service-target.js";
24
27
  import { activeOpAdditions } from "./pending.js";
25
28
  import { parseRequirements } from "../document/parse.js";
26
29
  import {} from "../document/spec.js";
@@ -44,66 +47,6 @@ export function coherenceLookups(scope, context) {
44
47
  const governedByLivingSpec = async (service, op) => {
45
48
  return (await livingRequirements(service)).some((r) => r.operations.includes(op));
46
49
  };
47
- // The enumerated fleet, read once and shared by both consumer scans below.
48
- let serviceIds;
49
- const enumeratedServices = async () => (serviceIds ??= await enumeratedServiceIds(docsDir, context));
50
- // --- retiring an operation the fleet still calls ---
51
- //
52
- // A removal marker is checked against the living CONTRACT (does the slot
53
- // exist) and the feature's own requirements (is the retirement governed).
54
- // Neither question asks the one that matters to the other ninety-nine repos:
55
- // is anybody still calling it? The living landscape is the fleet's own answer
56
- // — an edge with `metadata { op }` is a consumer somebody drew — and another
57
- // service's living requirements naming the operation are a second. Both are
58
- // gating: the merge deletes the operation from the contract while the edge
59
- // and the requirement stay, so the very next `validate --all` reports a
60
- // broken contract on a repository whose author was never in this feature.
61
- //
62
- // Lazy on purpose: the landscape is a full LikeC4 workspace spin-up and the
63
- // requirement scan reads every service's living spec, and a feature that
64
- // removes nothing must pay for neither.
65
- //
66
- // `core/diff/victims.ts` carries a second copy of this join (an index over
67
- // an already-read fleet — these closures are scoped to a DeltaScope and
68
- // cannot serve it). The victim strings and scan rules are kept in step by
69
- // hand; changing either module owes the other the same change, and a THIRD
70
- // copy forces the shared extraction (docs/DESIGN.md rule 13).
71
- let livingLandscape;
72
- const edgeConsumers = async (service, op) => {
73
- if (livingLandscape === undefined) {
74
- const path = landscapePath(docsDir);
75
- livingLandscape = existsSync(path)
76
- ? context === undefined
77
- ? await loadFile(path)
78
- : await context.loadLikeC4(path)
79
- : null;
80
- }
81
- // An unreadable landscape proves nothing either way; `landscape.invalid`
82
- // is validate's finding to make, and inventing a removal refusal out of a
83
- // parse error would point the author at the wrong file.
84
- if (livingLandscape === null || livingLandscape.errors.length > 0)
85
- return [];
86
- // The enumerated fleet rides along so a landscape that models CONTAINERS
87
- // stays visible: without it, an edge into `payment.api` resolves to a
88
- // service called "api" that has never existed, the `=== service` join
89
- // below finds nothing, and the one check standing between a removal and
90
- // its last consumer answers "nobody calls it".
91
- const resolve = serviceResolver(livingLandscape.elements, new Set(await enumeratedServices()));
92
- return livingLandscape.relationships
93
- .filter((r) => r.op === op && resolve(r.target) === service)
94
- .map((r) => `edge ${resolve(r.source)} → ${resolve(r.target)}${r.title === undefined ? "" : ` ("${r.title}")`}`);
95
- };
96
- const requirementConsumers = async (service, op) => {
97
- const others = (await enumeratedServices()).filter((id) => id !== service);
98
- const out = [];
99
- for (const other of others) {
100
- for (const r of await livingRequirements(other)) {
101
- if (r.operations.includes(op))
102
- out.push(`${other}'s living requirement '${r.name}'`);
103
- }
104
- }
105
- return out;
106
- };
107
50
  // What OTHER features in flight define, per (service, op). Cross-service work
108
51
  // normally lands as feature A calling an op that in-flight feature B introduces;
109
52
  // that is an ordering dependency, not a broken contract, and the requirements
@@ -147,9 +90,6 @@ export function coherenceLookups(scope, context) {
147
90
  }
148
91
  return set.has(op);
149
92
  };
150
- return {
151
- governedByLivingSpec, edgeConsumers, requirementConsumers, definedElsewhere,
152
- deprecatedInLiving, undeprecatedByFeature,
153
- };
93
+ return { governedByLivingSpec, definedElsewhere, deprecatedInLiving, undeprecatedByFeature };
154
94
  }
155
95
  //# sourceMappingURL=lookups.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lookups.js","sourceRoot":"","sources":["../../../src/core/coherence/lookups.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The questions the coherence checks ask lazily, each behind a per-service or\n * per-repo cache.\n *\n * All but one reach outside the delta — into a living spec, the fleet map, or\n * another feature in flight — and those are expensive: the landscape is a full\n * LikeC4 workspace spin-up, and the cross-feature scan is a walk of every\n * active delta. The exception is `undeprecatedByFeature`, which reads the\n * FEATURE's own openapi delta: it lives here anyway because it is the second\n * half of the one question `deprecatedInLiving` opens — \"is this op dying, and\n * is this feature the resurrection?\" — and splitting the pair across modules\n * would leave each half's cache discipline unexplained by the other. A feature\n * whose checks never need a lookup must not pay for it, which is why these are\n * closures over a cache rather than arguments computed up front.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { loadFile, type LoadedDoc } from \"../c4/likec4.js\";\nimport { serviceResolver } from \"../c4/resolve/service.js\";\nimport { type PathableService } from \"../kernel/ids/service.js\";\nimport { featureSpecPaths, landscapePath } from \"../repo/paths.js\";\nimport { operations } from \"../openapi/doc.js\";\nimport { enumeratedServiceIds, locateServicePaths } from \"../repo/service-target.js\";\nimport { activeOpAdditions } from \"./pending.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport { type Requirement } from \"../document/spec.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { type DeltaScope } from \"./declared.js\";\n\n/** What the checks may ask lazily — fleet questions, plus the deprecation pair. */\nexport interface Lookups {\n governedByLivingSpec(service: PathableService, op: string): Promise<boolean>;\n edgeConsumers(service: string, op: string): Promise<string[]>;\n requirementConsumers(service: string, op: string): Promise<string[]>;\n definedElsewhere(service: string, op: string): Promise<string | undefined>;\n deprecatedInLiving(service: PathableService, op: string): Promise<boolean>;\n undeprecatedByFeature(service: PathableService, op: string): Promise<boolean>;\n}\n\nexport function coherenceLookups(scope: DeltaScope, context?: FleetContext): Lookups {\n const { docsDir, featureDir, featureId } = scope;\n const livingReqs = new Map<string, Requirement[]>();\n const livingRequirements = async (service: PathableService): Promise<Requirement[]> => {\n let reqs = livingReqs.get(service);\n if (reqs === undefined) {\n const p = (await locateServicePaths(docsDir, service, context)).spec;\n reqs = existsSync(p)\n ? context === undefined\n ? parseRequirements(await readFile(p, \"utf8\"))\n : await context.readRequirements(p)\n : [];\n livingReqs.set(service, reqs);\n }\n return reqs;\n };\n const governedByLivingSpec = async (service: PathableService, op: string): Promise<boolean> => {\n return (await livingRequirements(service)).some((r) => r.operations.includes(op));\n };\n\n // The enumerated fleet, read once and shared by both consumer scans below.\n let serviceIds: PathableService[] | undefined;\n const enumeratedServices = async (): Promise<PathableService[]> =>\n (serviceIds ??= await enumeratedServiceIds(docsDir, context));\n\n // --- retiring an operation the fleet still calls ---\n //\n // A removal marker is checked against the living CONTRACT (does the slot\n // exist) and the feature's own requirements (is the retirement governed).\n // Neither question asks the one that matters to the other ninety-nine repos:\n // is anybody still calling it? The living landscape is the fleet's own answer\n // — an edge with `metadata { op }` is a consumer somebody drew — and another\n // service's living requirements naming the operation are a second. Both are\n // gating: the merge deletes the operation from the contract while the edge\n // and the requirement stay, so the very next `validate --all` reports a\n // broken contract on a repository whose author was never in this feature.\n //\n // Lazy on purpose: the landscape is a full LikeC4 workspace spin-up and the\n // requirement scan reads every service's living spec, and a feature that\n // removes nothing must pay for neither.\n //\n // `core/diff/victims.ts` carries a second copy of this join (an index over\n // an already-read fleet — these closures are scoped to a DeltaScope and\n // cannot serve it). The victim strings and scan rules are kept in step by\n // hand; changing either module owes the other the same change, and a THIRD\n // copy forces the shared extraction (docs/DESIGN.md rule 13).\n let livingLandscape: LoadedDoc | null | undefined;\n const edgeConsumers = async (service: string, op: string): Promise<string[]> => {\n if (livingLandscape === undefined) {\n const path = landscapePath(docsDir);\n livingLandscape = existsSync(path)\n ? context === undefined\n ? await loadFile(path)\n : await context.loadLikeC4(path)\n : null;\n }\n // An unreadable landscape proves nothing either way; `landscape.invalid`\n // is validate's finding to make, and inventing a removal refusal out of a\n // parse error would point the author at the wrong file.\n if (livingLandscape === null || livingLandscape.errors.length > 0) return [];\n // The enumerated fleet rides along so a landscape that models CONTAINERS\n // stays visible: without it, an edge into `payment.api` resolves to a\n // service called \"api\" that has never existed, the `=== service` join\n // below finds nothing, and the one check standing between a removal and\n // its last consumer answers \"nobody calls it\".\n const resolve = serviceResolver(livingLandscape.elements, new Set(await enumeratedServices()));\n return livingLandscape.relationships\n .filter((r) => r.op === op && resolve(r.target) === service)\n .map((r) => `edge ${resolve(r.source)} → ${resolve(r.target)}${r.title === undefined ? \"\" : ` (\"${r.title}\")`}`);\n };\n const requirementConsumers = async (service: string, op: string): Promise<string[]> => {\n const others = (await enumeratedServices()).filter((id) => id !== service);\n const out: string[] = [];\n for (const other of others) {\n for (const r of await livingRequirements(other)) {\n if (r.operations.includes(op)) out.push(`${other}'s living requirement '${r.name}'`);\n }\n }\n return out;\n };\n\n // What OTHER features in flight define, per (service, op). Cross-service work\n // normally lands as feature A calling an op that in-flight feature B introduces;\n // that is an ordering dependency, not a broken contract, and the requirements\n // axis already grades the same shape as a warn (delta.modified-pending). Lazy\n // like delta.ts's activeAdditions — the common case (op resolves) never pays\n // for the fleet scan.\n let inFlightOps: Map<string, string> | null = null;\n const definedElsewhere = async (service: string, op: string): Promise<string | undefined> => {\n inFlightOps ??= await activeOpAdditions(docsDir, featureId, context);\n return inFlightOps.get(`${service} ${op}`);\n };\n\n // What the LIVING provider contracts mark `deprecated: true`, read lazily\n // per service. Living only, on purpose: the feature's own openapi delta\n // restates the full API, and the question here is whether the fleet as\n // shipped is already retiring the op this feature starts leaning on.\n const livingDeprecated = new Map<string, Set<string>>();\n const deprecatedInLiving = async (service: PathableService, op: string): Promise<boolean> => {\n let set = livingDeprecated.get(service);\n if (!set) {\n const list = await operations((await locateServicePaths(docsDir, service, context)).openapi, context);\n set = new Set(list.filter((o) => o.deprecated).map((o) => o.id));\n livingDeprecated.set(service, set);\n }\n return set.has(op);\n };\n\n // ...unless this feature IS the un-deprecation: an openapi delta that\n // restates the op WITHOUT `deprecated: true` retires the flag on archive\n // (the path-item overwrite is wholesale), so \"prefer the replacement\n // operation\" would point the author away from the exact change they are\n // shipping. A delta that restates the op still deprecated — or has no\n // delta for the service at all — keeps the warning. The one lookup here\n // that reads the delta's own files rather than the world outside it (the\n // header says why it lives beside its living twin regardless).\n const featureUndeprecated = new Map<string, Set<string>>();\n const undeprecatedByFeature = async (service: PathableService, op: string): Promise<boolean> => {\n let set = featureUndeprecated.get(service);\n if (!set) {\n const list = await operations(featureSpecPaths(featureDir, service).openapi, context);\n set = new Set(list.filter((o) => !o.deprecated).map((o) => o.id));\n featureUndeprecated.set(service, set);\n }\n return set.has(op);\n };\n return {\n governedByLivingSpec, edgeConsumers, requirementConsumers, definedElsewhere,\n deprecatedInLiving, undeprecatedByFeature,\n };\n}\n"]}
1
+ {"version":3,"file":"lookups.js","sourceRoot":"","sources":["../../../src/core/coherence/lookups.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The questions the coherence checks ask lazily, each behind a per-service or\n * per-repo cache.\n *\n * All but one reach outside the delta — into a living spec, a living\n * contract, or another feature in flight — and the cross-feature scan is a\n * walk of every active delta. The exception is `undeprecatedByFeature`, which\n * reads the FEATURE's own openapi delta: it lives here anyway because it is the\n * second half of the one question `deprecatedInLiving` opens — \"is this op\n * dying, and is this feature the resurrection?\" — and splitting the pair across\n * modules would leave each half's cache discipline unexplained by the other. A\n * feature whose checks never need a lookup must not pay for it, which is why\n * these are closures over a cache rather than arguments computed up front.\n *\n * The one question that used to live here and does not — who still CONSUMES an\n * operation the feature takes out (`edgeConsumers`, `requirementConsumers`,\n * `governorsAfterMerge`) — is the consumer index's now (`core/consumers/`),\n * because `loam diff` and the archive planner ask it too and the three copies\n * had drifted apart in which documents they read.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { type PathableService } from \"../kernel/ids/service.js\";\nimport { featureSpecPaths } from \"../repo/paths.js\";\nimport { operations } from \"../openapi/doc.js\";\nimport { locateServicePaths } from \"../repo/service-target.js\";\nimport { activeOpAdditions } from \"./pending.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport { type Requirement } from \"../document/spec.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { type DeltaScope } from \"./declared.js\";\n\n/** What the checks may ask lazily — fleet questions, plus the deprecation pair. */\nexport interface Lookups {\n governedByLivingSpec(service: PathableService, op: string): Promise<boolean>;\n definedElsewhere(service: string, op: string): Promise<string | undefined>;\n deprecatedInLiving(service: PathableService, op: string): Promise<boolean>;\n undeprecatedByFeature(service: PathableService, op: string): Promise<boolean>;\n}\n\nexport function coherenceLookups(scope: DeltaScope, context?: FleetContext): Lookups {\n const { docsDir, featureDir, featureId } = scope;\n const livingReqs = new Map<string, Requirement[]>();\n const livingRequirements = async (service: PathableService): Promise<Requirement[]> => {\n let reqs = livingReqs.get(service);\n if (reqs === undefined) {\n const p = (await locateServicePaths(docsDir, service, context)).spec;\n reqs = existsSync(p)\n ? context === undefined\n ? parseRequirements(await readFile(p, \"utf8\"))\n : await context.readRequirements(p)\n : [];\n livingReqs.set(service, reqs);\n }\n return reqs;\n };\n const governedByLivingSpec = async (service: PathableService, op: string): Promise<boolean> => {\n return (await livingRequirements(service)).some((r) => r.operations.includes(op));\n };\n\n // What OTHER features in flight define, per (service, op). Cross-service work\n // normally lands as feature A calling an op that in-flight feature B introduces;\n // that is an ordering dependency, not a broken contract, and the requirements\n // axis already grades the same shape as a warn (delta.modified-pending). Lazy\n // like delta.ts's activeAdditions — the common case (op resolves) never pays\n // for the fleet scan.\n let inFlightOps: Map<string, string> | null = null;\n const definedElsewhere = async (service: string, op: string): Promise<string | undefined> => {\n inFlightOps ??= await activeOpAdditions(docsDir, featureId, context);\n return inFlightOps.get(`${service} ${op}`);\n };\n\n // What the LIVING provider contracts mark `deprecated: true`, read lazily\n // per service. Living only, on purpose: the feature's own openapi delta\n // restates the full API, and the question here is whether the fleet as\n // shipped is already retiring the op this feature starts leaning on.\n const livingDeprecated = new Map<string, Set<string>>();\n const deprecatedInLiving = async (service: PathableService, op: string): Promise<boolean> => {\n let set = livingDeprecated.get(service);\n if (!set) {\n const list = await operations((await locateServicePaths(docsDir, service, context)).openapi, context);\n set = new Set(list.filter((o) => o.deprecated).map((o) => o.id));\n livingDeprecated.set(service, set);\n }\n return set.has(op);\n };\n\n // ...unless this feature IS the un-deprecation: an openapi delta that\n // restates the op WITHOUT `deprecated: true` retires the flag on archive\n // (the path-item overwrite is wholesale), so \"prefer the replacement\n // operation\" would point the author away from the exact change they are\n // shipping. A delta that restates the op still deprecated — or has no\n // delta for the service at all — keeps the warning. The one lookup here\n // that reads the delta's own files rather than the world outside it (the\n // header says why it lives beside its living twin regardless).\n const featureUndeprecated = new Map<string, Set<string>>();\n const undeprecatedByFeature = async (service: PathableService, op: string): Promise<boolean> => {\n let set = featureUndeprecated.get(service);\n if (!set) {\n const list = await operations(featureSpecPaths(featureDir, service).openapi, context);\n set = new Set(list.filter((o) => !o.deprecated).map((o) => o.id));\n featureUndeprecated.set(service, set);\n }\n return set.has(op);\n };\n return { governedByLivingSpec, definedElsewhere, deprecatedInLiving, undeprecatedByFeature };\n}\n"]}