@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
@@ -7,7 +7,8 @@
7
7
  * delta facts it already holds (tagged relationships and the element
8
8
  * resolver): ./declared.ts walks the feature's own contracts and grades
9
9
  * what one service's pair of documents can settle; ./lookups.ts answers the
10
- * fleet questions lazily; this module joins the three axes — the exact
10
+ * in-flight question lazily, and the consumer index (core/consumers/) who
11
+ * still consumes a message; this module joins the three axes — the exact
11
12
  * shape coherence.ts gives the operation join, on the message name.
12
13
  *
13
14
  * A package of its own rather than lines in coherence.ts because that file
@@ -16,17 +17,100 @@
16
17
  * there does.
17
18
  */
18
19
  import {} from "../../c4/likec4.js";
20
+ import { eventKeys, unboundSentence } from "../../c4/direction/direction.js";
19
21
  import {} from "../../kernel/ids/service.js";
20
22
  import { locateServicePaths } from "../../repo/service-target.js";
21
23
  import { enumeratedServiceIndex } from "../../repo/service-target.js";
22
24
  import { readAsyncapi } from "../../asyncapi/read.js";
23
25
  import { declaredEvents } from "./declared.js";
24
26
  import { eventLookups } from "./lookups.js";
27
+ import { revivedEvents } from "../retired/revival/revived.js";
28
+ import { nothingStill, refusalDetails, refusalMessage, stillNamed } from "../consumed/refusal.js";
29
+ import { fleetSendersAfter, unproducedIssues } from "./produced.js";
25
30
  export async function eventCoherence(request) {
26
- const { scope, svcNames, taggedRels, svcOf, context } = request;
31
+ const { scope, svcNames, taggedRels, svcOf, context, consumers } = request;
27
32
  const issues = [];
28
33
  const declared = await declaredEvents(scope, svcNames, issues, context);
29
34
  const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);
35
+ // Who would still name a message after this merge: the consumer index's
36
+ // consumers, and its `own` — the provider's own `Publishes:` lines as the
37
+ // merge leaves them. That is the event twin of the overwrite's own-requirement
38
+ // scan: a message re-keyed or no longer sent while the provider's living
39
+ // requirement still says it `Publishes:` it archived at exit 0, and the next
40
+ // `validate --all` reported `spec-event.message-undefined` against the
41
+ // provider (review 2026-10-12, finding 3) — a REMOVED requirement justifies a
42
+ // marker, but nothing asks a re-key or a turned-around operation for one.
43
+ // A consumer document nobody could read or place refuses too, as on the
44
+ // contract axis (../consumed/refusal.ts).
45
+ //
46
+ // But a CONSUMER is stranded only when nothing would send the message after
47
+ // this merge — no service, and no #external element whose edge publishes it.
48
+ // That is the rule `spine.message-unproduced` grades a consumer by — it stays
49
+ // quiet over an outside producer (`externalProducerOf`,
50
+ // core/c4/resolve/external.ts) — and the one `loam diff` asks first
51
+ // (`producers`, then `externalProducer`, core/diff/semantic.ts).
52
+ // Counting every consumer "whoever its producer is" refused a retirement from
53
+ // one of two senders over the other sender's own consumes edge, and told the
54
+ // author to drop it, while `loam diff` called the same removal a warning
55
+ // ("still sent by …") and the next `validate --all` was clean (the
56
+ // verification of the consumer fixes, N2). Counting the fleet's senders alone
57
+ // then refused a retirement from a message's only fleet sender while an
58
+ // #external element still published it, and `loam diff` called the archived
59
+ // removal `diff.message-removed-consumed`, while the next `validate --all` was
60
+ // clean again (the verification of N1–N3, D1). The provider's OWN documents are
61
+ // another matter: the spine and validate grade them against the provider's
62
+ // own contract, so they strand whoever else sends the message, and `own`
63
+ // still refuses beside a surviving sender.
64
+ //
65
+ // The senders are asked of the MERGE, never of the living fleet alone:
66
+ // `producers` reads the fleet as it stands, so a feature retiring the message
67
+ // from both senders would let each one pass on the other's living contract.
68
+ // A service this feature carries an event delta for sends what the
69
+ // simulation leaves it (`declared.sentAfter`), or vouches for nothing; every
70
+ // other service sends what its living contract does. Positive evidence only,
71
+ // as `producers` is: an unreadable contract, or a merge that stood down,
72
+ // never keeps a message produced. The outside producer is the living map's
73
+ // edge and tag, which the landscape merge cannot take away (it only adds to
74
+ // the map), asked over the directories the merge leaves: each `specs/<svc>/`
75
+ // of this feature becomes a `services/<svc>/`, and a directory the #external
76
+ // element resolves to outranks its tag, so a feature onboarding the outside
77
+ // system as a service takes the producer away. Asked over the directories
78
+ // as they stood, the gate passed that retirement while the next `loam diff`
79
+ // reported `diff.message-removed-consumed` and `validate --all`
80
+ // `spine.message-unproduced` (the verification of D1, X2). One this
81
+ // feature's own C4 delta would draw is not counted, so that shape errs
82
+ // toward refusing — `--approve`, or draw the edge in the living map. The
83
+ // consumer check in ./produced.ts DOES count it, deliberately: there the
84
+ // closed direction is the other one (that module says why), and the fleet
85
+ // half of the senders is the one helper both read.
86
+ const sendersAfter = async (svc, message) => {
87
+ const fleet = (await fleetSendersAfter(declared, consumers, message)).filter((id) => id !== svc);
88
+ const outside = await consumers.externalProducer(message);
89
+ return outside === null ? fleet : [...fleet, `${outside} (#external)`];
90
+ };
91
+ const still = async (svc, message) => {
92
+ const answer = await consumers.messageConsumers(svc, message);
93
+ const senders = await sendersAfter(svc, message);
94
+ return { senders, found: stillNamed(senders.length === 0 ? answer : { ...answer, victims: [], suspended: [] }) };
95
+ };
96
+ // The landscape merge only ADDS edges, so an edge is dropped in the living map
97
+ // itself — a consumer's consumes edge, or the provider's own publishes edge,
98
+ // which the spine grades on its source (`own`, core/consumers/consumers.ts).
99
+ const route = `drop its consumes edge, or drop or redraw its publishes edge, in the architecture/ project, MODIFY or REMOVE the requirement whose Consumes:/Publishes: line names it in this feature's specs/<service>/ delta`;
100
+ // With a sender left only the provider's own documents are named, so the
101
+ // remedy is theirs alone — telling the author to drop a consumer's edge
102
+ // there is the false route this gate used to give.
103
+ const ownRoute = (svc) => `drop or redraw its publishes edge in the architecture/ project, or MODIFY or REMOVE the requirement whose Publishes: line names it in this feature's specs/${svc}/ delta`;
104
+ const ownOnly = (svc, senders) => `${senders.join(", ")} would still send it after this merge, so its consumers keep a producer, but ${svc}'s own documents would still name it`;
105
+ // The route a consumer's refusal gains: another sender keeps the message
106
+ // produced, and one this feature adds counts (`declared.sentAfter`); so does
107
+ // the #external element that publishes it, drawn in the living map — the one
108
+ // place `sendersAfter` reads an outside producer from — unless this feature
109
+ // makes it a service. The route says so: a feature onboarding the gateway is
110
+ // told no #external element publishes the message while its living map
111
+ // draws one doing exactly that, and the way out is the first route, the new
112
+ // service's own send operation in its specs/<service>/ delta.
113
+ const anotherSender = "keep another service sending it (an action: send operation in its asyncapi.yaml, living or in this feature's specs/<service>/ delta) or an #external element publishing it (a publishes edge from it in the living architecture/ project; one whose element this feature's specs/<service>/ makes a service no longer counts)";
30
114
  // Retiring a message the fleet still consumes — openapi.remove-op-consumed's
31
115
  // twin, and like it a question neither exactness nor justification asks:
32
116
  // the merge deletes the declaration while the consumes edge and the other
@@ -34,14 +118,23 @@ export async function eventCoherence(request) {
34
118
  // repository whose author was never in this feature.
35
119
  for (const [svc, net] of declared.netRemoved) {
36
120
  for (const message of net) {
37
- const consumers = await lookups.messageConsumers(svc, message);
38
- if (consumers.length === 0)
121
+ const { found, senders } = await still(svc, message);
122
+ if (nothingStill(found))
39
123
  continue;
124
+ const lost = `this feature retires message '${message}'`;
40
125
  issues.push({
41
126
  severity: "error",
42
127
  code: "asyncapi.remove-message-consumed",
43
128
  subject: svc,
44
- message: `${svc}: this feature retires message '${message}', but the living fleet still consumes it — ${consumers.join("; ")}. Retire the consumer in the same feature (drop the consumes edge from architecture/landscape.likec4, or the requirement that names it), or archive with --approve to break it deliberately.`,
129
+ details: refusalDetails(found),
130
+ message: refusalMessage(found, {
131
+ subject: svc,
132
+ lost,
133
+ verb: "name",
134
+ named: (list) => senders.length === 0
135
+ ? `${svc}: ${lost}, and no readable contract would still send it after this merge, nor does an #external element publish it, but the fleet would still name it — ${list}. Retire each in the same change (${route}), ${anotherSender}, or archive with --approve to break it deliberately.`
136
+ : `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,
137
+ }),
45
138
  });
46
139
  }
47
140
  }
@@ -56,17 +149,37 @@ export async function eventCoherence(request) {
56
149
  for (const message of stopped) {
57
150
  if (net?.has(message) === true)
58
151
  continue;
59
- const consumers = await lookups.messageConsumers(svc, message);
60
- if (consumers.length === 0)
152
+ const { found, senders } = await still(svc, message);
153
+ if (nothingStill(found))
61
154
  continue;
155
+ const lost = `after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}'`;
62
156
  issues.push({
63
157
  severity: "error",
64
158
  code: "asyncapi.remove-message-consumed",
65
159
  subject: svc,
66
- message: `${svc}: after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}', but the living fleet still consumes it — ${consumers.join("; ")}. Keep an operation sending it, retire the consumer in the same feature, or archive with --approve to break it deliberately.`,
160
+ details: refusalDetails(found),
161
+ message: refusalMessage(found, {
162
+ subject: svc,
163
+ lost,
164
+ verb: "name",
165
+ named: (list) => senders.length === 0
166
+ ? `${svc}: ${lost}, and no other readable contract would send it either, nor does an #external element publish it, but the fleet would still name it — ${list}. Keep an operation sending it, ${anotherSender}, retire each in the same change (${route}), or archive with --approve to break it deliberately.`
167
+ : `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Keep an operation sending it, retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,
168
+ }),
67
169
  });
68
170
  }
69
171
  }
172
+ // A revival: a slot or message this delta declares that the living
173
+ // contract no longer has because an ARCHIVED feature retired it, or a
174
+ // message name declared again at a key an archived feature took it out of
175
+ // — the contract axis's `openapi.op-revived`, on the slot and the message
176
+ // name (../retired/revival/revived.ts). Asked of the index only when the merged
177
+ // contract declares something the living one does not.
178
+ for (const [svc, arrived] of declared.revivalCandidates) {
179
+ if (arrived.slots.length === 0 && arrived.messages.length === 0)
180
+ continue;
181
+ issues.push(...revivedEvents(svc, arrived, await request.retired()));
182
+ }
70
183
  // Two features declaring one (service, message): both deltas apply
71
184
  // cleanly, so whichever archives second replaces the other's declaration
72
185
  // wholesale — delta.added-conflict's shape on the message join.
@@ -133,21 +246,35 @@ export async function eventCoherence(request) {
133
246
  const enumeratedTarget = enumeratedServiceIndex(scope.docsDir, svcNames, context);
134
247
  // Tagged edges: `publishes` binds the edge's SOURCE, `consumes` its
135
248
  // TARGET — the same reading validate's event spine takes of the living
136
- // landscape, applied to the delta.
249
+ // landscape, applied to the delta, from the one statement of the rule
250
+ // (core/c4/direction/direction.ts).
251
+ // Every consumption this merge would add, collected as the two loops below
252
+ // walk the edges and the lines, and asked for a producer at the end
253
+ // (./produced.ts) — `spine.message-unproduced` at feature scope.
254
+ const claims = [];
137
255
  for (const r of taggedRels) {
138
- const bound = [];
139
256
  const edge = `${svcOf(r.source)} → ${svcOf(r.target)}`;
140
- if (r.publishes !== undefined)
141
- bound.push({ direction: "publishes", message: r.publishes, name: svcOf(r.source), edge });
142
- if (r.consumes !== undefined)
143
- bound.push({ direction: "consumes", message: r.consumes, name: svcOf(r.target), edge });
144
- for (const b of bound) {
257
+ for (const key of eventKeys(r)) {
258
+ const b = { direction: key.direction, message: key.message, name: svcOf(key.bound), edge };
145
259
  const pathable = await enumeratedTarget(b.name);
146
- if (pathable === undefined)
260
+ if (pathable === undefined) {
261
+ // The skip above, unless the key is bound to infrastructure and a
262
+ // service sits at the OTHER end: then it is on the wrong end of its
263
+ // edge and binds nothing, which the living spine reports under the
264
+ // same code once this delta merges. An `#external` system at the bound
265
+ // end is an outside producer or consumer, and stays skipped.
266
+ const kind = request.infraKindOf(key.bound);
267
+ const drawer = kind === undefined ? undefined : await enumeratedTarget(svcOf(key.other));
268
+ if (kind !== undefined && drawer !== undefined) {
269
+ issues.push({ severity: "warn", code: "spine.message-unbound", subject: drawer, message: `delta ${unboundSentence(r, key, kind)}` });
270
+ }
147
271
  continue;
272
+ }
148
273
  // asyncapi.invalid already gates; one breach, one finding.
149
274
  if (declared.unreadable.has(pathable))
150
275
  continue;
276
+ if (b.direction === "consumes")
277
+ claims.push({ service: pathable, message: b.message, source: "edge", via: `edge ${b.edge}` });
151
278
  const ok = await declares(pathable, b.direction, b.message);
152
279
  if (ok !== false)
153
280
  continue;
@@ -177,6 +304,9 @@ export async function eventCoherence(request) {
177
304
  if (declared.unreadable.has(svc))
178
305
  continue;
179
306
  for (const link of declared.links.get(svc) ?? []) {
307
+ if (link.direction === "consumes") {
308
+ claims.push({ service: svc, message: link.message, source: "requirement", via: `requirement '${link.requirement}'` });
309
+ }
180
310
  const ok = await declares(svc, link.direction, link.message);
181
311
  if (ok !== false)
182
312
  continue;
@@ -199,6 +329,7 @@ export async function eventCoherence(request) {
199
329
  }
200
330
  }
201
331
  }
332
+ issues.push(...(await unproducedIssues({ scope, declared, consumers, lookups, claims, known: request.known, ...(context === undefined ? {} : { context }) })));
202
333
  return issues;
203
334
  }
204
335
  //# sourceMappingURL=events.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/events.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The event axis of the feature gate: do the C4 delta's tagged edges, the\n * requirement deltas' `Publishes:`/`Consumes:` lines, and the asyncapi\n * contracts agree about what each service puts on the wire?\n *\n * The package's orchestrator, called once from featureCoherence with the\n * delta facts it already holds (tagged relationships and the element\n * resolver): ./declared.ts walks the feature's own contracts and grades\n * what one service's pair of documents can settle; ./lookups.ts answers the\n * fleet questions lazily; this module joins the three axes — the exact\n * shape coherence.ts gives the operation join, on the message name.\n *\n * A package of its own rather than lines in coherence.ts because that file\n * ran at its 300-line limit before the event axis existed; the seam is\n * real, not cosmetic — everything here joins on a message name, nothing\n * there does.\n */\nimport { type Rel } from \"../../c4/likec4.js\";\nimport { type PathableService } from \"../../kernel/ids/service.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { enumeratedServiceIndex } from \"../../repo/service-target.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport { declaredEvents, type EventDeltaScope } from \"./declared.js\";\nimport { eventLookups } from \"./lookups.js\";\n\n/** What featureCoherence hands the event axis — the delta facts it already computed. */\nexport interface EventCoherenceRequest {\n scope: EventDeltaScope;\n /** The services this feature's specs/ touch — the walk's subjects. */\n svcNames: PathableService[];\n /** The delta's tagged relationships — the edges the landscape merge would splice. */\n taggedRels: Rel[];\n /** The element→service resolver over the delta's elements plus the enumerated fleet. */\n svcOf: (id: string) => string;\n context?: FleetContext;\n}\n\nexport async function eventCoherence(request: EventCoherenceRequest): Promise<Issue[]> {\n const { scope, svcNames, taggedRels, svcOf, context } = request;\n const issues: Issue[] = [];\n const declared = await declaredEvents(scope, svcNames, issues, context);\n const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);\n\n // Retiring a message the fleet still consumes — openapi.remove-op-consumed's\n // twin, and like it a question neither exactness nor justification asks:\n // the merge deletes the declaration while the consumes edge and the other\n // repo's requirement stay, so the next `validate --all` breaks on a\n // repository whose author was never in this feature.\n for (const [svc, net] of declared.netRemoved) {\n for (const message of net) {\n const consumers = await lookups.messageConsumers(svc, message);\n if (consumers.length === 0) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n message: `${svc}: this feature retires message '${message}', but the living fleet still consumes it — ${consumers.join(\"; \")}. Retire the consumer in the same feature (drop the consumes edge from architecture/landscape.likec4, or the requirement that names it), or archive with --approve to break it deliberately.`,\n });\n }\n }\n\n // The WIRE half of the same question: a channel or operation removal (or\n // an edit) can stop the service SENDING a message whose declaration\n // survives — the merge applies cleanly, and the next `validate --all`\n // breaks on the consumer's repository (spine.message-unproduced) exactly\n // as a deleted declaration would. Asked of the simulated merge's sent-set\n // diff; a name the net-removal loop above already asked keeps one finding.\n for (const [svc, stopped] of declared.stoppedSending) {\n const net = declared.netRemoved.get(svc);\n for (const message of stopped) {\n if (net?.has(message) === true) continue;\n const consumers = await lookups.messageConsumers(svc, message);\n if (consumers.length === 0) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n message: `${svc}: after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}', but the living fleet still consumes it — ${consumers.join(\"; \")}. Keep an operation sending it, retire the consumer in the same feature, or archive with --approve to break it deliberately.`,\n });\n }\n }\n\n // Two features declaring one (service, message): both deltas apply\n // cleanly, so whichever archives second replaces the other's declaration\n // wholesale — delta.added-conflict's shape on the message join.\n for (const [svc, doc] of declared.featureDocs) {\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const other = await lookups.messageDefinedElsewhere(svc, m.name);\n if (other === undefined) continue;\n issues.push({\n severity: \"warn\",\n code: \"asyncapi.message-conflict\",\n subject: svc,\n message: `${svc}: this feature declares message '${m.name}', which in-flight ${other} also adds or edits — whichever archives second replaces the other's declaration wholesale; coordinate before either ships`,\n });\n }\n }\n\n // The bound service's contract as the feature would leave it. Where the\n // feature carries a delta over a readable living contract, that is the\n // SIMULATED merge (declared.mergedDocs): a slot the delta retires or edits\n // away stops answering here BEFORE archive — a leftover Publishes: line on\n // a surviving requirement used to pass this gate on living's say-so, then\n // break `validate --all` on the post-archive docs. Everywhere the\n // simulation has no answer — no delta for the service, a side that does\n // not read, a document the merge refuses — the feature ∪ living union\n // stands in, which can only be too lenient, never wrongly red; the living\n // side is read lazily per service, since an edge may bind a service the\n // feature carries no specs/ directory for.\n const livingDocs = new Map<PathableService, AsyncapiDoc>();\n const livingOf = async (svc: PathableService): Promise<AsyncapiDoc> => {\n let doc = livingDocs.get(svc);\n if (doc === undefined) {\n doc = await readAsyncapi((await locateServicePaths(scope.docsDir, svc, context)).asyncapi, context);\n livingDocs.set(svc, doc);\n }\n return doc;\n };\n // undefined = no verdict: an unreadable LIVING contract proves nothing\n // about the message (validate's asyncapi.invalid owns that file), and\n // grading against its empty parse would blame every edge for one broken\n // document.\n const declares = async (\n svc: PathableService,\n direction: \"publishes\" | \"consumes\",\n message: string,\n ): Promise<boolean | undefined> => {\n const merged = declared.mergedDocs.get(svc);\n if (merged !== undefined) {\n return direction === \"publishes\" ? merged.sent.includes(message) : merged.received.includes(message);\n }\n const living = await livingOf(svc);\n if (living.unreadable) return undefined;\n const feat = declared.featureDocs.get(svc);\n return direction === \"publishes\"\n ? living.sent.includes(message) || (feat?.sent.includes(message) ?? false)\n : living.received.includes(message) || (feat?.received.includes(message) ?? false);\n };\n const action = (direction: \"publishes\" | \"consumes\"): string => (direction === \"publishes\" ? \"send\" : \"receive\");\n\n // The declared→enumerated bridge coherence.ts uses for op edges, for the\n // same reason: every lookup below joins into services/<id>/ or the\n // feature's own specs/. A bound element NEITHER knows is skipped rather\n // than graded — an #external producer is the documented shape for a\n // message produced outside the fleet (spine.message-external), so grading\n // its edge against an absent service directory would turn the worked\n // example red.\n const enumeratedTarget = enumeratedServiceIndex(scope.docsDir, svcNames, context);\n\n // Tagged edges: `publishes` binds the edge's SOURCE, `consumes` its\n // TARGET — the same reading validate's event spine takes of the living\n // landscape, applied to the delta.\n for (const r of taggedRels) {\n const bound: { direction: \"publishes\" | \"consumes\"; message: string; name: string; edge: string }[] = [];\n const edge = `${svcOf(r.source)} → ${svcOf(r.target)}`;\n if (r.publishes !== undefined) bound.push({ direction: \"publishes\", message: r.publishes, name: svcOf(r.source), edge });\n if (r.consumes !== undefined) bound.push({ direction: \"consumes\", message: r.consumes, name: svcOf(r.target), edge });\n for (const b of bound) {\n const pathable = await enumeratedTarget(b.name);\n if (pathable === undefined) continue;\n // asyncapi.invalid already gates; one breach, one finding.\n if (declared.unreadable.has(pathable)) continue;\n const ok = await declares(pathable, b.direction, b.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(pathable, b.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"c4-event.message-pending\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"c4-event.message-undefined\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', but ${b.name}'s asyncapi contract (feature and living alike) declares no operation with action: ${action(b.direction)} for it`,\n });\n }\n }\n }\n\n // The requirement deltas' own lines, graded the same way — the same\n // sentence validate gives the living-side breach, under the same code, so\n // one breach keeps one name across scopes. The lines were read once by\n // ./declared.ts's walk (declared.links); re-reading them here per service\n // was a second parse of both spec files on the context-less archive path.\n for (const svc of svcNames) {\n if (declared.unreadable.has(svc)) continue;\n for (const link of declared.links.get(svc) ?? []) {\n const ok = await declares(svc, link.direction, link.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(svc, link.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"spec-event.message-pending\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"spec-event.message-undefined\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', but ${svc}'s asyncapi.yaml declares no operation with action: ${action(link.direction)} for it`,\n });\n }\n }\n }\n\n return issues;\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/events.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The event axis of the feature gate: do the C4 delta's tagged edges, the\n * requirement deltas' `Publishes:`/`Consumes:` lines, and the asyncapi\n * contracts agree about what each service puts on the wire?\n *\n * The package's orchestrator, called once from featureCoherence with the\n * delta facts it already holds (tagged relationships and the element\n * resolver): ./declared.ts walks the feature's own contracts and grades\n * what one service's pair of documents can settle; ./lookups.ts answers the\n * in-flight question lazily, and the consumer index (core/consumers/) who\n * still consumes a message; this module joins the three axes — the exact\n * shape coherence.ts gives the operation join, on the message name.\n *\n * A package of its own rather than lines in coherence.ts because that file\n * ran at its 300-line limit before the event axis existed; the seam is\n * real, not cosmetic — everything here joins on a message name, nothing\n * there does.\n */\nimport { type Rel } from \"../../c4/likec4.js\";\nimport { eventKeys, unboundSentence } from \"../../c4/direction/direction.js\";\nimport { type PathableService, type RawServiceId } from \"../../kernel/ids/service.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { enumeratedServiceIndex } from \"../../repo/service-target.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport { declaredEvents, type EventDeltaScope } from \"./declared.js\";\nimport { eventLookups } from \"./lookups.js\";\nimport type { RetiredLookup } from \"../retired/archived.js\";\nimport { revivedEvents } from \"../retired/revival/revived.js\";\nimport type { ConsumerIndex } from \"../../consumers/consumers.js\";\nimport { nothingStill, refusalDetails, refusalMessage, stillNamed, type StillNamed } from \"../consumed/refusal.js\";\nimport { fleetSendersAfter, unproducedIssues, type ConsumeClaim } from \"./produced.js\";\n\n/** What featureCoherence hands the event axis — the delta facts it already computed. */\nexport interface EventCoherenceRequest {\n scope: EventDeltaScope;\n /** The services this feature's specs/ touch — the walk's subjects. */\n svcNames: PathableService[];\n /** The delta's tagged relationships — the edges the landscape merge would splice. */\n taggedRels: Rel[];\n /** The element→service resolver over the delta's elements plus the enumerated fleet. */\n svcOf: (id: string) => string;\n /** The infrastructure kind of a delta element or its ancestor (core/c4/direction/direction.ts) — a key bound there is on the wrong end. */\n infraKindOf: (id: string) => string | undefined;\n context?: FleetContext;\n /** The retired index, shared with the contract axis so one call walks `features/archive/` once at most. */\n retired: RetiredLookup;\n /** The consumer index, read after this feature's merge — shared with the contract axis so the fleet is read once. */\n consumers: ConsumerIndex;\n /** The services the merge leaves a directory for — the enumerated fleet plus this feature's `specs/` (./produced.ts). */\n known: ReadonlySet<RawServiceId>;\n}\n\nexport async function eventCoherence(request: EventCoherenceRequest): Promise<Issue[]> {\n const { scope, svcNames, taggedRels, svcOf, context, consumers } = request;\n const issues: Issue[] = [];\n const declared = await declaredEvents(scope, svcNames, issues, context);\n const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);\n\n // Who would still name a message after this merge: the consumer index's\n // consumers, and its `own` — the provider's own `Publishes:` lines as the\n // merge leaves them. That is the event twin of the overwrite's own-requirement\n // scan: a message re-keyed or no longer sent while the provider's living\n // requirement still says it `Publishes:` it archived at exit 0, and the next\n // `validate --all` reported `spec-event.message-undefined` against the\n // provider (review 2026-10-12, finding 3) — a REMOVED requirement justifies a\n // marker, but nothing asks a re-key or a turned-around operation for one.\n // A consumer document nobody could read or place refuses too, as on the\n // contract axis (../consumed/refusal.ts).\n //\n // But a CONSUMER is stranded only when nothing would send the message after\n // this merge — no service, and no #external element whose edge publishes it.\n // That is the rule `spine.message-unproduced` grades a consumer by — it stays\n // quiet over an outside producer (`externalProducerOf`,\n // core/c4/resolve/external.ts) — and the one `loam diff` asks first\n // (`producers`, then `externalProducer`, core/diff/semantic.ts).\n // Counting every consumer \"whoever its producer is\" refused a retirement from\n // one of two senders over the other sender's own consumes edge, and told the\n // author to drop it, while `loam diff` called the same removal a warning\n // (\"still sent by …\") and the next `validate --all` was clean (the\n // verification of the consumer fixes, N2). Counting the fleet's senders alone\n // then refused a retirement from a message's only fleet sender while an\n // #external element still published it, and `loam diff` called the archived\n // removal `diff.message-removed-consumed`, while the next `validate --all` was\n // clean again (the verification of N1–N3, D1). The provider's OWN documents are\n // another matter: the spine and validate grade them against the provider's\n // own contract, so they strand whoever else sends the message, and `own`\n // still refuses beside a surviving sender.\n //\n // The senders are asked of the MERGE, never of the living fleet alone:\n // `producers` reads the fleet as it stands, so a feature retiring the message\n // from both senders would let each one pass on the other's living contract.\n // A service this feature carries an event delta for sends what the\n // simulation leaves it (`declared.sentAfter`), or vouches for nothing; every\n // other service sends what its living contract does. Positive evidence only,\n // as `producers` is: an unreadable contract, or a merge that stood down,\n // never keeps a message produced. The outside producer is the living map's\n // edge and tag, which the landscape merge cannot take away (it only adds to\n // the map), asked over the directories the merge leaves: each `specs/<svc>/`\n // of this feature becomes a `services/<svc>/`, and a directory the #external\n // element resolves to outranks its tag, so a feature onboarding the outside\n // system as a service takes the producer away. Asked over the directories\n // as they stood, the gate passed that retirement while the next `loam diff`\n // reported `diff.message-removed-consumed` and `validate --all`\n // `spine.message-unproduced` (the verification of D1, X2). One this\n // feature's own C4 delta would draw is not counted, so that shape errs\n // toward refusing — `--approve`, or draw the edge in the living map. The\n // consumer check in ./produced.ts DOES count it, deliberately: there the\n // closed direction is the other one (that module says why), and the fleet\n // half of the senders is the one helper both read.\n const sendersAfter = async (svc: string, message: string): Promise<string[]> => {\n const fleet = (await fleetSendersAfter(declared, consumers, message)).filter((id) => id !== svc);\n const outside = await consumers.externalProducer(message);\n return outside === null ? fleet : [...fleet, `${outside} (#external)`];\n };\n const still = async (svc: string, message: string): Promise<{ found: StillNamed; senders: string[] }> => {\n const answer = await consumers.messageConsumers(svc, message);\n const senders = await sendersAfter(svc, message);\n return { senders, found: stillNamed(senders.length === 0 ? answer : { ...answer, victims: [], suspended: [] }) };\n };\n // The landscape merge only ADDS edges, so an edge is dropped in the living map\n // itself — a consumer's consumes edge, or the provider's own publishes edge,\n // which the spine grades on its source (`own`, core/consumers/consumers.ts).\n const route = `drop its consumes edge, or drop or redraw its publishes edge, in the architecture/ project, MODIFY or REMOVE the requirement whose Consumes:/Publishes: line names it in this feature's specs/<service>/ delta`;\n // With a sender left only the provider's own documents are named, so the\n // remedy is theirs alone — telling the author to drop a consumer's edge\n // there is the false route this gate used to give.\n const ownRoute = (svc: string): string =>\n `drop or redraw its publishes edge in the architecture/ project, or MODIFY or REMOVE the requirement whose Publishes: line names it in this feature's specs/${svc}/ delta`;\n const ownOnly = (svc: string, senders: string[]): string =>\n `${senders.join(\", \")} would still send it after this merge, so its consumers keep a producer, but ${svc}'s own documents would still name it`;\n // The route a consumer's refusal gains: another sender keeps the message\n // produced, and one this feature adds counts (`declared.sentAfter`); so does\n // the #external element that publishes it, drawn in the living map — the one\n // place `sendersAfter` reads an outside producer from — unless this feature\n // makes it a service. The route says so: a feature onboarding the gateway is\n // told no #external element publishes the message while its living map\n // draws one doing exactly that, and the way out is the first route, the new\n // service's own send operation in its specs/<service>/ delta.\n const anotherSender =\n \"keep another service sending it (an action: send operation in its asyncapi.yaml, living or in this feature's specs/<service>/ delta) or an #external element publishing it (a publishes edge from it in the living architecture/ project; one whose element this feature's specs/<service>/ makes a service no longer counts)\";\n\n // Retiring a message the fleet still consumes — openapi.remove-op-consumed's\n // twin, and like it a question neither exactness nor justification asks:\n // the merge deletes the declaration while the consumes edge and the other\n // repo's requirement stay, so the next `validate --all` breaks on a\n // repository whose author was never in this feature.\n for (const [svc, net] of declared.netRemoved) {\n for (const message of net) {\n const { found, senders } = await still(svc, message);\n if (nothingStill(found)) continue;\n const lost = `this feature retires message '${message}'`;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n details: refusalDetails(found),\n message: refusalMessage(found, {\n subject: svc,\n lost,\n verb: \"name\",\n named: (list) =>\n senders.length === 0\n ? `${svc}: ${lost}, and no readable contract would still send it after this merge, nor does an #external element publish it, but the fleet would still name it — ${list}. Retire each in the same change (${route}), ${anotherSender}, or archive with --approve to break it deliberately.`\n : `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,\n }),\n });\n }\n }\n\n // The WIRE half of the same question: a channel or operation removal (or\n // an edit) can stop the service SENDING a message whose declaration\n // survives — the merge applies cleanly, and the next `validate --all`\n // breaks on the consumer's repository (spine.message-unproduced) exactly\n // as a deleted declaration would. Asked of the simulated merge's sent-set\n // diff; a name the net-removal loop above already asked keeps one finding.\n for (const [svc, stopped] of declared.stoppedSending) {\n const net = declared.netRemoved.get(svc);\n for (const message of stopped) {\n if (net?.has(message) === true) continue;\n const { found, senders } = await still(svc, message);\n if (nothingStill(found)) continue;\n const lost = `after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}'`;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n details: refusalDetails(found),\n message: refusalMessage(found, {\n subject: svc,\n lost,\n verb: \"name\",\n named: (list) =>\n senders.length === 0\n ? `${svc}: ${lost}, and no other readable contract would send it either, nor does an #external element publish it, but the fleet would still name it — ${list}. Keep an operation sending it, ${anotherSender}, retire each in the same change (${route}), or archive with --approve to break it deliberately.`\n : `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Keep an operation sending it, retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,\n }),\n });\n }\n }\n\n // A revival: a slot or message this delta declares that the living\n // contract no longer has because an ARCHIVED feature retired it, or a\n // message name declared again at a key an archived feature took it out of\n // — the contract axis's `openapi.op-revived`, on the slot and the message\n // name (../retired/revival/revived.ts). Asked of the index only when the merged\n // contract declares something the living one does not.\n for (const [svc, arrived] of declared.revivalCandidates) {\n if (arrived.slots.length === 0 && arrived.messages.length === 0) continue;\n issues.push(...revivedEvents(svc, arrived, await request.retired()));\n }\n\n // Two features declaring one (service, message): both deltas apply\n // cleanly, so whichever archives second replaces the other's declaration\n // wholesale — delta.added-conflict's shape on the message join.\n for (const [svc, doc] of declared.featureDocs) {\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const other = await lookups.messageDefinedElsewhere(svc, m.name);\n if (other === undefined) continue;\n issues.push({\n severity: \"warn\",\n code: \"asyncapi.message-conflict\",\n subject: svc,\n message: `${svc}: this feature declares message '${m.name}', which in-flight ${other} also adds or edits — whichever archives second replaces the other's declaration wholesale; coordinate before either ships`,\n });\n }\n }\n\n // The bound service's contract as the feature would leave it. Where the\n // feature carries a delta over a readable living contract, that is the\n // SIMULATED merge (declared.mergedDocs): a slot the delta retires or edits\n // away stops answering here BEFORE archive — a leftover Publishes: line on\n // a surviving requirement used to pass this gate on living's say-so, then\n // break `validate --all` on the post-archive docs. Everywhere the\n // simulation has no answer — no delta for the service, a side that does\n // not read, a document the merge refuses — the feature ∪ living union\n // stands in, which can only be too lenient, never wrongly red; the living\n // side is read lazily per service, since an edge may bind a service the\n // feature carries no specs/ directory for.\n const livingDocs = new Map<PathableService, AsyncapiDoc>();\n const livingOf = async (svc: PathableService): Promise<AsyncapiDoc> => {\n let doc = livingDocs.get(svc);\n if (doc === undefined) {\n doc = await readAsyncapi((await locateServicePaths(scope.docsDir, svc, context)).asyncapi, context);\n livingDocs.set(svc, doc);\n }\n return doc;\n };\n // undefined = no verdict: an unreadable LIVING contract proves nothing\n // about the message (validate's asyncapi.invalid owns that file), and\n // grading against its empty parse would blame every edge for one broken\n // document.\n const declares = async (\n svc: PathableService,\n direction: \"publishes\" | \"consumes\",\n message: string,\n ): Promise<boolean | undefined> => {\n const merged = declared.mergedDocs.get(svc);\n if (merged !== undefined) {\n return direction === \"publishes\" ? merged.sent.includes(message) : merged.received.includes(message);\n }\n const living = await livingOf(svc);\n if (living.unreadable) return undefined;\n const feat = declared.featureDocs.get(svc);\n return direction === \"publishes\"\n ? living.sent.includes(message) || (feat?.sent.includes(message) ?? false)\n : living.received.includes(message) || (feat?.received.includes(message) ?? false);\n };\n const action = (direction: \"publishes\" | \"consumes\"): string => (direction === \"publishes\" ? \"send\" : \"receive\");\n\n // The declared→enumerated bridge coherence.ts uses for op edges, for the\n // same reason: every lookup below joins into services/<id>/ or the\n // feature's own specs/. A bound element NEITHER knows is skipped rather\n // than graded — an #external producer is the documented shape for a\n // message produced outside the fleet (spine.message-external), so grading\n // its edge against an absent service directory would turn the worked\n // example red.\n const enumeratedTarget = enumeratedServiceIndex(scope.docsDir, svcNames, context);\n\n // Tagged edges: `publishes` binds the edge's SOURCE, `consumes` its\n // TARGET — the same reading validate's event spine takes of the living\n // landscape, applied to the delta, from the one statement of the rule\n // (core/c4/direction/direction.ts).\n // Every consumption this merge would add, collected as the two loops below\n // walk the edges and the lines, and asked for a producer at the end\n // (./produced.ts) — `spine.message-unproduced` at feature scope.\n const claims: ConsumeClaim[] = [];\n for (const r of taggedRels) {\n const edge = `${svcOf(r.source)} → ${svcOf(r.target)}`;\n for (const key of eventKeys(r)) {\n const b = { direction: key.direction, message: key.message, name: svcOf(key.bound), edge };\n const pathable = await enumeratedTarget(b.name);\n if (pathable === undefined) {\n // The skip above, unless the key is bound to infrastructure and a\n // service sits at the OTHER end: then it is on the wrong end of its\n // edge and binds nothing, which the living spine reports under the\n // same code once this delta merges. An `#external` system at the bound\n // end is an outside producer or consumer, and stays skipped.\n const kind = request.infraKindOf(key.bound);\n const drawer = kind === undefined ? undefined : await enumeratedTarget(svcOf(key.other));\n if (kind !== undefined && drawer !== undefined) {\n issues.push({ severity: \"warn\", code: \"spine.message-unbound\", subject: drawer, message: `delta ${unboundSentence(r, key, kind)}` });\n }\n continue;\n }\n // asyncapi.invalid already gates; one breach, one finding.\n if (declared.unreadable.has(pathable)) continue;\n if (b.direction === \"consumes\") claims.push({ service: pathable, message: b.message, source: \"edge\", via: `edge ${b.edge}` });\n const ok = await declares(pathable, b.direction, b.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(pathable, b.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"c4-event.message-pending\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"c4-event.message-undefined\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', but ${b.name}'s asyncapi contract (feature and living alike) declares no operation with action: ${action(b.direction)} for it`,\n });\n }\n }\n }\n\n // The requirement deltas' own lines, graded the same way — the same\n // sentence validate gives the living-side breach, under the same code, so\n // one breach keeps one name across scopes. The lines were read once by\n // ./declared.ts's walk (declared.links); re-reading them here per service\n // was a second parse of both spec files on the context-less archive path.\n for (const svc of svcNames) {\n if (declared.unreadable.has(svc)) continue;\n for (const link of declared.links.get(svc) ?? []) {\n if (link.direction === \"consumes\") {\n claims.push({ service: svc, message: link.message, source: \"requirement\", via: `requirement '${link.requirement}'` });\n }\n const ok = await declares(svc, link.direction, link.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(svc, link.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"spec-event.message-pending\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"spec-event.message-undefined\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', but ${svc}'s asyncapi.yaml declares no operation with action: ${action(link.direction)} for it`,\n });\n }\n }\n }\n\n issues.push(...(await unproducedIssues({ scope, declared, consumers, lookups, claims, known: request.known, ...(context === undefined ? {} : { context }) })));\n return issues;\n}\n"]}
@@ -1,6 +1,7 @@
1
- import { ASYNCAPI_BASELINES_KEY } from "../../asyncapi/digest.js";
2
- import {} from "../../asyncapi/model.js";
1
+ import { ASYNCAPI_BASELINES_KEY, slotKey } from "../../asyncapi/digest.js";
2
+ import { moveBackRemedy } from "../retired/revival/revived.js";
3
3
  import { asyncapiSurfaces } from "../../asyncapi/baseline/surfaces.js";
4
+ import { messageName } from "../../asyncapi/read.js";
4
5
  import { entryFor, readBaselineRecord, surfaceIn } from "../../openapi/baseline/record.js";
5
6
  import { classifyBaselineDigests, OPERATION_DIGEST_LENGTH, OPERATION_DIGEST_RE, valueDigest, } from "../../openapi/digest.js";
6
7
  /**
@@ -11,7 +12,7 @@ import { classifyBaselineDigests, OPERATION_DIGEST_LENGTH, OPERATION_DIGEST_RE,
11
12
  * graded once or the two halves drift into different ideas of what it restates.
12
13
  */
13
14
  export function gradeBaselines(input, issues) {
14
- const { featSlots, livingSlots, svc, featureId } = input;
15
+ const { featSlots, livingSlots, livingDoc, svc, featureId } = input;
15
16
  let unpinned = 0;
16
17
  for (const slot of featSlots) {
17
18
  // A removal marker is not a restatement of anything; its own exactness
@@ -19,7 +20,7 @@ export function gradeBaselines(input, issues) {
19
20
  if (slot.remove)
20
21
  continue;
21
22
  const label = `${slot.section}.${slot.key}`;
22
- const living = livingSlots.get(`${slot.section}\0${slot.key}`);
23
+ const living = livingSlots.get(slotKey(slot));
23
24
  if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {
24
25
  issues.push({
25
26
  severity: "error",
@@ -31,11 +32,33 @@ export function gradeBaselines(input, issues) {
31
32
  }
32
33
  const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);
33
34
  if (verdict === "unfounded") {
35
+ // openapi/baseline/gate.ts's reading, on the slot: a pin is only ever
36
+ // written over a living slot, so one with nothing behind it usually
37
+ // means a parallel feature retired the slot — and "drop the marker",
38
+ // which this used to say, archived the quote as an addition and put the
39
+ // retired slot back. Or MOVED it, on the one slot a move vacates on
40
+ // this axis: a `components.messages` key whose message name the living
41
+ // contract now declares elsewhere. A channel or operation slot's
42
+ // identity is its key, so it is never "moved" — it is retired. The
43
+ // move-back remedy is the revival finding's own (../retired/revival/revived.ts):
44
+ // where the name now stands inline under a channel, a marker there
45
+ // retires nothing, so the remedy names the channel instead. The two
46
+ // places are said as a condition ("while it is still declared there"),
47
+ // not as what this delta does: one that already restates or retires
48
+ // that channel takes the name out of it, and only the pin is left.
49
+ const name = slot.section === "components.messages" ? messageName(slot.node, slot.key) : undefined;
50
+ const movedTo = livingDoc.messages.filter((m) => m.remove !== true && m.name === name).map((m) => m.slot);
51
+ // The channels still pointing at a key the remedy retires are read off
52
+ // the contract the merge would leave; where it refuses (and the archive
53
+ // with it), off the living one, which is what the merge would start from.
54
+ const remedy = () => moveBackRemedy(movedTo, label, input.merged()?.root ?? input.livingPlain);
34
55
  issues.push({
35
56
  severity: "error",
36
57
  code: "asyncapi.baseline-invalid",
37
58
  subject: svc,
38
- message: `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — a new channel, operation or message has no living version to be based on; drop the marker`,
59
+ message: movedTo.length > 0
60
+ ? `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — message '${name}' is declared at ${movedTo.join(" and ")} now: a parallel feature moved it there (or retired it here, and another re-declared it there). Re-read it there and delete this slot from the delta, making any change at its new key; archived while it is still declared there, it would declare '${name}' at ${label} as well. To move it back on purpose, drop the pin and ${remedy()} in this delta.`
61
+ : `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — the version it was pinned to is gone. If a parallel feature retired it, re-read the living contract and delete this slot from the delta, unless this feature deliberately brings it back (then drop the pin and archive with --approve past \`asyncapi.slot-revived\`). A genuinely new channel, operation or message has no living version to be based on and carries no pin.`,
39
62
  });
40
63
  }
41
64
  else if (verdict === "stale") {
@@ -129,13 +152,13 @@ function gradeSurfaces(input, issues) {
129
152
  return unpinned;
130
153
  }
131
154
  /** Marker exactness: the slot must exist, and a message marker must name the living declaration. */
132
- export function gradeMarkers(input, svc, issues) {
133
- const { featDoc, livingDoc, featSlots, livingSlots } = input;
155
+ export function gradeMarkers(pair, svc, issues) {
156
+ const { featDoc, livingDoc, featSlots, livingSlots } = pair;
134
157
  for (const slot of featSlots) {
135
158
  if (!slot.remove)
136
159
  continue;
137
160
  const label = `${slot.section}.${slot.key}`;
138
- const living = livingSlots.get(`${slot.section}\0${slot.key}`);
161
+ const living = livingSlots.get(slotKey(slot));
139
162
  if (living === undefined) {
140
163
  issues.push({
141
164
  severity: "error",
@@ -1 +1 @@
1
- {"version":3,"file":"grades.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/grades.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,sBAAsB,EAAqB,MAAM,0BAA0B,CAAC;AACrF,OAAO,EAAoB,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC3F,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,WAAW,GACZ,MAAM,yBAAyB,CAAC;AAcjC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAA4B,EAAE,MAAe;IAC1E,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACzD,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,uEAAuE;QACvE,yBAAyB;QACzB,IAAI,IAAI,CAAC,MAAM;YAAE,SAAS;QAC1B,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/D,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1E,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,2BAA2B;gBACjC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,KAAK,KAAK,iCAAiC,IAAI,CAAC,OAAO,gBAAgB,uBAAuB,2DAA2D;aACzK,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACnF,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,2BAA2B;gBACjC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,KAAK,KAAK,iKAAiK;aAC3L,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,KAAK,KAAK,uCAAuC,IAAI,CAAC,OAAO,uCAAuC,MAAO,CAAC,MAAM,8KAA8K,SAAS,KAAK;aAC9T,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,KAAK,UAAU,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1D,+DAA+D;YAC/D,mEAAmE;YACnE,4DAA4D;YAC5D,QAAQ,IAAI,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IACD,uEAAuE;IACvE,uEAAuE;IACvE,wEAAwE;IACxE,kDAAkD;IAClD,QAAQ,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC;YACV,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,2BAA2B;YACjC,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,GAAG,GAAG,KAAK,QAAQ,8GAA8G,sBAAsB,iMAAiM,SAAS,cAAc,GAAG,+DAA+D;SAC3b,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,aAAa,CAAC,KAA4B,EAAE,MAAe;IAClE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACzD,MAAM,OAAO,GAAG,CAAC,OAAe,EAAQ,EAAE;QACxC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,GAAG,KAAK,OAAO,EAAE,EAAE,CAAC,CAAC;IACrH,CAAC,CAAC;IACF,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC3D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,OAAO,0GAA0G,SAAS,kBAAkB,CAAC,CAAC;IAC3J,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC7C,yEAAyE;IACzE,yEAAyE;IACzE,0EAA0E;IAC1E,4EAA4E;IAC5E,6EAA6E;IAC7E,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC/B,OAAO,CAAC,GAAG,sBAAsB,oBAAoB,EAAE,kFAAkF,SAAS,0BAA0B,CAAC,CAAC;IAChL,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,sBAAsB,oBAAoB,IAAI,kGAAkG,SAAS,0BAA0B,CAAC,CAAC;IAClM,CAAC;IAED,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,uBAAuB,CACrC,KAAK,EACL,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,EACpB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CACrD,CAAC;QACF,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;YAC3B,uEAAuE;YACvE,kEAAkE;YAClE,IAAI,MAAM,CAAC,KAAK;gBAAE,QAAQ,IAAI,CAAC,CAAC;QAClC,CAAC;aAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,gBAAgB,CAAC,CAAC,EAAE,kCAAkC,KAAK,uCAAuC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,+KAA+K,SAAS,KAAK;aACxU,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;YACnC,OAAO,CAAC,cAAc,CAAC,CAAC,EAAE,kBAAkB,KAAK,oJAAoJ,SAAS,IAAI,CAAC,CAAC;QACtN,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,YAAY,CAC1B,KAA0H,EAC1H,GAAW,EACX,MAAe;IAEf,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAC7D,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,SAAS;QAC3B,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC/D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,gCAAgC;gBACtC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,uBAAuB,KAAK,0JAA0J;aACtM,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,qBAAqB;YAAE,SAAS;QACrD,kEAAkE;QAClE,mEAAmE;QACnE,oEAAoE;QACpE,gDAAgD;QAChD,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC;QACzG,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC;QACtF,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,uBAAuB,KAAK,mBAAmB,UAAU,2CAA2C,UAAU,+DAA+D;aAC7L,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["/**\n * The per-slot grades of one service's pair of event contracts: baseline\n * pins and removal-marker exactness. Split from ./declared.ts along the\n * subject seam — that module owns the WALK (which files, which services,\n * which sets), these functions own the verdict each slot gets once both\n * sides are in hand — when the walk grew past the line limit; the split\n * moved them verbatim.\n */\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { ASYNCAPI_BASELINES_KEY, type AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport { type AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport { asyncapiSurfaces } from \"../../asyncapi/baseline/surfaces.js\";\nimport { entryFor, readBaselineRecord, surfaceIn } from \"../../openapi/baseline/record.js\";\nimport {\n classifyBaselineDigests,\n OPERATION_DIGEST_LENGTH,\n OPERATION_DIGEST_RE,\n valueDigest,\n} from \"../../openapi/digest.js\";\n\n/** Everything the two baseline graders need about one service's pair of contracts. */\nexport interface AsyncapiBaselineInput {\n featSlots: AsyncapiSlot[];\n livingSlots: Map<string, AsyncapiSlot>;\n /** The FEATURE document's resolved parse — where the record and the surfaces are read from. */\n featPlain: unknown;\n /** The LIVING document's resolved parse; `{}` where no living contract exists. */\n livingPlain: unknown;\n svc: string;\n featureId: string;\n}\n\n/**\n * One service's baselines against the living contract — openapi/baseline/gate.ts's\n * verdicts, over BOTH halves of this axis: the slot pins in-value, and the\n * component surfaces the root `x-loam-baselines` record pins. One function\n * because the unpinned count is shared (see below), and because a delta is\n * graded once or the two halves drift into different ideas of what it restates.\n */\nexport function gradeBaselines(input: AsyncapiBaselineInput, issues: Issue[]): void {\n const { featSlots, livingSlots, svc, featureId } = input;\n let unpinned = 0;\n for (const slot of featSlots) {\n // A removal marker is not a restatement of anything; its own exactness\n // checks guard the slot.\n if (slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(`${slot.section}\\0${slot.key}`);\n if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} has invalid x-loam-based-on '${slot.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n continue;\n }\n const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);\n if (verdict === \"unfounded\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — a new channel, operation or message has no living version to be based on; drop the marker`,\n });\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: ${label} was written against living version ${slot.basedOn}, but the living contract now holds ${living!.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living slot, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unpinned\" && living !== undefined) {\n // Counted, not listed — a delta quotes most of the contract it\n // restates, and one warning per quote teaches people to filter the\n // code out (openapi.baseline-missing's doctrine, verbatim).\n unpinned += 1;\n }\n }\n // The surface half's unpinned count folds into the SAME counter, not a\n // second warning: \"counted, not listed\" is the doctrine above, and one\n // warning per axis-half would have a delta over a schema-heavy contract\n // print two findings naming the same one command.\n unpinned += gradeSurfaces(input, issues);\n if (unpinned > 0) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"asyncapi.baseline-missing\",\n subject: svc,\n message: `${svc}: ${unpinned} slot(s) and component surface(s) in this feature's asyncapi.yaml carry no baseline pin (x-loam-based-on / ${ASYNCAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \\`loam rebase ${featureId} --service ${svc}\\`, or archive with --approve to merge unpinned deliberately.`,\n });\n }\n}\n\n/**\n * The surface half: every `components/<kind>/<name>` outside `messages`,\n * graded from the root `x-loam-baselines` record by the same classifier the\n * slots go through. Returns the unpinned count so the caller can fold it into\n * the one per-service warning rather than emitting a second.\n *\n * The codes are REUSED rather than multiplied — `asyncapi.baseline-invalid`\n * and `asyncapi.baseline-stale` say exactly the same thing about a surface as\n * about a slot, and a caller that branches on them wants one branch.\n */\nfunction gradeSurfaces(input: AsyncapiBaselineInput, issues: Issue[]): number {\n const { featPlain, livingPlain, svc, featureId } = input;\n const invalid = (message: string): void => {\n issues.push({ severity: \"error\", code: \"asyncapi.baseline-invalid\", subject: svc, message: `${svc}: ${message}` });\n };\n const { record, problems } = readBaselineRecord(featPlain);\n for (const problem of problems) {\n invalid(`${problem} — the record is \\`loam rebase\\`'s bookkeeping and cannot be graded as it stands; re-run \\`loam rebase ${featureId}\\` to rebuild it`);\n }\n const surfaces = asyncapiSurfaces(featPlain);\n // An entry pinning a surface the delta does not declare is a claim about\n // nothing — usually one the author deleted after rebasing. Distinct from\n // `unfounded` below, where the surface IS declared and the LIVING side is\n // what vanished. A `pathItems` section is the same fault at the axis level:\n // an AsyncAPI document has no path items, so the whole section pins nothing,\n // and rebase's wholesale rebuild would drop it without a word.\n const declared = new Set(surfaces.map((s) => s.id));\n for (const id of Object.keys(record.components)) {\n if (declared.has(id)) continue;\n invalid(`${ASYNCAPI_BASELINES_KEY} pins component '${id}', but this feature's asyncapi.yaml does not declare it — re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n for (const path of Object.keys(record.pathItems)) {\n invalid(`${ASYNCAPI_BASELINES_KEY} pins path item '${path}', but an AsyncAPI document has no path items — the section pins nothing; re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n\n let unpinned = 0;\n for (const s of surfaces) {\n const entry = entryFor(record, s);\n const living = surfaceIn(livingPlain, s);\n const verdict = classifyBaselineDigests(\n entry,\n valueDigest(s.value),\n living.found ? valueDigest(living.value) : undefined,\n );\n if (verdict === \"unpinned\") {\n // Only a surface the living contract ALREADY has can be reverted by an\n // unpinned merge; a genuinely new schema never trips the counter.\n if (living.found) unpinned += 1;\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: component '${s.id}' was pinned to living version ${entry}, but the living contract now holds ${valueDigest(living.value)} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unfounded\") {\n invalid(`component '${s.id}' is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \\`loam rebase ${featureId}\\``);\n }\n }\n return unpinned;\n}\n\n/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */\nexport function gradeMarkers(\n input: { featDoc: AsyncapiDoc; livingDoc: AsyncapiDoc; featSlots: AsyncapiSlot[]; livingSlots: Map<string, AsyncapiSlot> },\n svc: string,\n issues: Issue[],\n): void {\n const { featDoc, livingDoc, featSlots, livingSlots } = input;\n for (const slot of featSlots) {\n if (!slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(`${slot.section}\\0${slot.key}`);\n if (living === undefined) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker at ${label} addresses no living slot — the contract has nothing there to retire; update the stale marker to the current key, or drop it if the slot is already gone`,\n });\n continue;\n }\n if (slot.section !== \"components.messages\") continue;\n // Removal is exact for messages the way it is for operations: the\n // marker names both the key and the message identity it expects to\n // delete, so a slot whose NAME moved under the delta is caught here\n // rather than deleting somebody else's message.\n const markerName = featDoc.messages.find((m) => m.remove === true && m.slot === label)?.name ?? slot.key;\n const livingName = livingDoc.messages.find((m) => m.slot === label)?.name ?? slot.key;\n if (markerName !== livingName) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker at ${label} names message '${markerName}', but the living declaration there is '${livingName}' — loam never deletes a different message occupying the slot`,\n });\n }\n }\n}\n"]}
1
+ {"version":3,"file":"grades.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/grades.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The per-slot grades of one service's pair of event contracts: baseline\n * pins and removal-marker exactness. Split from ./declared.ts along the\n * subject seam — that module owns the WALK (which files, which services,\n * which sets), these functions own the verdict each slot gets once both\n * sides are in hand — when the walk grew past the line limit; the split\n * moved them verbatim.\n */\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { ASYNCAPI_BASELINES_KEY, slotKey } from \"../../asyncapi/digest.js\";\nimport type { EventPair, EventPairRead } from \"../retired/event-pair.js\";\nimport { moveBackRemedy } from \"../retired/revival/revived.js\";\nimport { asyncapiSurfaces } from \"../../asyncapi/baseline/surfaces.js\";\nimport { messageName } from \"../../asyncapi/read.js\";\nimport { entryFor, readBaselineRecord, surfaceIn } from \"../../openapi/baseline/record.js\";\nimport {\n classifyBaselineDigests,\n OPERATION_DIGEST_LENGTH,\n OPERATION_DIGEST_RE,\n valueDigest,\n} from \"../../openapi/digest.js\";\n\n/** Everything the two baseline graders need about one service's pair of contracts. */\nexport interface AsyncapiBaselineInput extends EventPair, Pick<EventPairRead, \"merged\"> {\n /** The FEATURE document's resolved parse — where the record and the surfaces are read from. */\n featPlain: unknown;\n /** The LIVING document's resolved parse; `{}` where no living contract exists. */\n livingPlain: unknown;\n svc: string;\n featureId: string;\n}\n\n/**\n * One service's baselines against the living contract — openapi/baseline/gate.ts's\n * verdicts, over BOTH halves of this axis: the slot pins in-value, and the\n * component surfaces the root `x-loam-baselines` record pins. One function\n * because the unpinned count is shared (see below), and because a delta is\n * graded once or the two halves drift into different ideas of what it restates.\n */\nexport function gradeBaselines(input: AsyncapiBaselineInput, issues: Issue[]): void {\n const { featSlots, livingSlots, livingDoc, svc, featureId } = input;\n let unpinned = 0;\n for (const slot of featSlots) {\n // A removal marker is not a restatement of anything; its own exactness\n // checks guard the slot.\n if (slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(slotKey(slot));\n if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} has invalid x-loam-based-on '${slot.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n continue;\n }\n const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);\n if (verdict === \"unfounded\") {\n // openapi/baseline/gate.ts's reading, on the slot: a pin is only ever\n // written over a living slot, so one with nothing behind it usually\n // means a parallel feature retired the slot — and \"drop the marker\",\n // which this used to say, archived the quote as an addition and put the\n // retired slot back. Or MOVED it, on the one slot a move vacates on\n // this axis: a `components.messages` key whose message name the living\n // contract now declares elsewhere. A channel or operation slot's\n // identity is its key, so it is never \"moved\" — it is retired. The\n // move-back remedy is the revival finding's own (../retired/revival/revived.ts):\n // where the name now stands inline under a channel, a marker there\n // retires nothing, so the remedy names the channel instead. The two\n // places are said as a condition (\"while it is still declared there\"),\n // not as what this delta does: one that already restates or retires\n // that channel takes the name out of it, and only the pin is left.\n const name = slot.section === \"components.messages\" ? messageName(slot.node, slot.key) : undefined;\n const movedTo = livingDoc.messages.filter((m) => m.remove !== true && m.name === name).map((m) => m.slot);\n // The channels still pointing at a key the remedy retires are read off\n // the contract the merge would leave; where it refuses (and the archive\n // with it), off the living one, which is what the merge would start from.\n const remedy = (): string => moveBackRemedy(movedTo, label, input.merged()?.root ?? input.livingPlain);\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: movedTo.length > 0\n ? `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — message '${name}' is declared at ${movedTo.join(\" and \")} now: a parallel feature moved it there (or retired it here, and another re-declared it there). Re-read it there and delete this slot from the delta, making any change at its new key; archived while it is still declared there, it would declare '${name}' at ${label} as well. To move it back on purpose, drop the pin and ${remedy()} in this delta.`\n : `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — the version it was pinned to is gone. If a parallel feature retired it, re-read the living contract and delete this slot from the delta, unless this feature deliberately brings it back (then drop the pin and archive with --approve past \\`asyncapi.slot-revived\\`). A genuinely new channel, operation or message has no living version to be based on and carries no pin.`,\n });\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: ${label} was written against living version ${slot.basedOn}, but the living contract now holds ${living!.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living slot, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unpinned\" && living !== undefined) {\n // Counted, not listed — a delta quotes most of the contract it\n // restates, and one warning per quote teaches people to filter the\n // code out (openapi.baseline-missing's doctrine, verbatim).\n unpinned += 1;\n }\n }\n // The surface half's unpinned count folds into the SAME counter, not a\n // second warning: \"counted, not listed\" is the doctrine above, and one\n // warning per axis-half would have a delta over a schema-heavy contract\n // print two findings naming the same one command.\n unpinned += gradeSurfaces(input, issues);\n if (unpinned > 0) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"asyncapi.baseline-missing\",\n subject: svc,\n message: `${svc}: ${unpinned} slot(s) and component surface(s) in this feature's asyncapi.yaml carry no baseline pin (x-loam-based-on / ${ASYNCAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \\`loam rebase ${featureId} --service ${svc}\\`, or archive with --approve to merge unpinned deliberately.`,\n });\n }\n}\n\n/**\n * The surface half: every `components/<kind>/<name>` outside `messages`,\n * graded from the root `x-loam-baselines` record by the same classifier the\n * slots go through. Returns the unpinned count so the caller can fold it into\n * the one per-service warning rather than emitting a second.\n *\n * The codes are REUSED rather than multiplied — `asyncapi.baseline-invalid`\n * and `asyncapi.baseline-stale` say exactly the same thing about a surface as\n * about a slot, and a caller that branches on them wants one branch.\n */\nfunction gradeSurfaces(input: AsyncapiBaselineInput, issues: Issue[]): number {\n const { featPlain, livingPlain, svc, featureId } = input;\n const invalid = (message: string): void => {\n issues.push({ severity: \"error\", code: \"asyncapi.baseline-invalid\", subject: svc, message: `${svc}: ${message}` });\n };\n const { record, problems } = readBaselineRecord(featPlain);\n for (const problem of problems) {\n invalid(`${problem} — the record is \\`loam rebase\\`'s bookkeeping and cannot be graded as it stands; re-run \\`loam rebase ${featureId}\\` to rebuild it`);\n }\n const surfaces = asyncapiSurfaces(featPlain);\n // An entry pinning a surface the delta does not declare is a claim about\n // nothing — usually one the author deleted after rebasing. Distinct from\n // `unfounded` below, where the surface IS declared and the LIVING side is\n // what vanished. A `pathItems` section is the same fault at the axis level:\n // an AsyncAPI document has no path items, so the whole section pins nothing,\n // and rebase's wholesale rebuild would drop it without a word.\n const declared = new Set(surfaces.map((s) => s.id));\n for (const id of Object.keys(record.components)) {\n if (declared.has(id)) continue;\n invalid(`${ASYNCAPI_BASELINES_KEY} pins component '${id}', but this feature's asyncapi.yaml does not declare it — re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n for (const path of Object.keys(record.pathItems)) {\n invalid(`${ASYNCAPI_BASELINES_KEY} pins path item '${path}', but an AsyncAPI document has no path items — the section pins nothing; re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n\n let unpinned = 0;\n for (const s of surfaces) {\n const entry = entryFor(record, s);\n const living = surfaceIn(livingPlain, s);\n const verdict = classifyBaselineDigests(\n entry,\n valueDigest(s.value),\n living.found ? valueDigest(living.value) : undefined,\n );\n if (verdict === \"unpinned\") {\n // Only a surface the living contract ALREADY has can be reverted by an\n // unpinned merge; a genuinely new schema never trips the counter.\n if (living.found) unpinned += 1;\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: component '${s.id}' was pinned to living version ${entry}, but the living contract now holds ${valueDigest(living.value)} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unfounded\") {\n invalid(`component '${s.id}' is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \\`loam rebase ${featureId}\\``);\n }\n }\n return unpinned;\n}\n\n/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */\nexport function gradeMarkers(pair: EventPair, svc: string, issues: Issue[]): void {\n const { featDoc, livingDoc, featSlots, livingSlots } = pair;\n for (const slot of featSlots) {\n if (!slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(slotKey(slot));\n if (living === undefined) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker at ${label} addresses no living slot — the contract has nothing there to retire; update the stale marker to the current key, or drop it if the slot is already gone`,\n });\n continue;\n }\n if (slot.section !== \"components.messages\") continue;\n // Removal is exact for messages the way it is for operations: the\n // marker names both the key and the message identity it expects to\n // delete, so a slot whose NAME moved under the delta is caught here\n // rather than deleting somebody else's message.\n const markerName = featDoc.messages.find((m) => m.remove === true && m.slot === label)?.name ?? slot.key;\n const livingName = livingDoc.messages.find((m) => m.slot === label)?.name ?? slot.key;\n if (markerName !== livingName) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker at ${label} names message '${markerName}', but the living declaration there is '${livingName}' — loam never deletes a different message occupying the slot`,\n });\n }\n }\n}\n"]}