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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -0,0 +1,285 @@
1
+ /**
2
+ * The retired index: what every ARCHIVED feature took out of the fleet's living
3
+ * contracts, and who took it.
4
+ *
5
+ * The question it answers is one the living contract cannot: is an operation
6
+ * the living contract does not have NEW, or was it RETIRED? Both read as
7
+ * absent. Before this index existed the feature gate could only treat absent
8
+ * as new — so a delta quoting an operation that a parallel feature retired and
9
+ * archived first was graded as an addition (`api.op-unconsumed`, advisory) and
10
+ * archived at exit 0, putting the retired operation straight back into the
11
+ * living contract. The baseline gate's unpinned count (openapi/baseline/gate.ts)
12
+ * could not see it either: it counts only quotes of operations the living
13
+ * contract still has, so that a genuinely new operation never trips it.
14
+ *
15
+ * The record of a retirement is files, not history. An archived feature keeps
16
+ * its delta under `features/archive/<FEAT>/specs/<svc>/`, and the removal
17
+ * markers in it say what that merge was ASKED to delete — read through the
18
+ * same rule the gate grades an in-flight delta by (./retirement.ts), so
19
+ * relocations are not retirements here either. No git: the docs semantics stay
20
+ * a property of the tree.
21
+ *
22
+ * Asked is not done. The OpenAPI merge deletes only when the living operation
23
+ * AT the marker's method and path carries the marker's operationId
24
+ * (openapi/merge/merge.ts); the event merge deletes a channel or operation slot
25
+ * only when the living contract has that key, and a `components.messages` slot
26
+ * only when the message declared there carries the marker's name
27
+ * (asyncapi/merge/merge.ts). A marker that missed — `remove-target-missing` or
28
+ * `remove-target-mismatch`, archived past under `--approve` — deleted nothing,
29
+ * and counting it called a genuinely new operation a revival and refused it.
30
+ * So every marker is checked against the contract the merge ran against: the
31
+ * feature's snapshot pre-image (`.loam-before/`, staging/snapshot/manifest.ts).
32
+ * A contract the archive CREATED, or never wrote at all, lost nothing to that
33
+ * merge, and reads as an empty pre-image. "Never wrote" is the event merge's
34
+ * case alone: it returns no text when it changes nothing (an asyncapi delta
35
+ * whose every marker missed), and archive snapshots only what it writes. The
36
+ * OpenAPI merge returns text whenever the delta has a path entry, so a
37
+ * contract it left unchanged has an ordinary pre-image equal to what it
38
+ * wrote — read like any other, and every missed marker misses it again. A
39
+ * feature with no intact snapshot (archived before the snapshot existed, or
40
+ * the snapshot edited or removed since) cannot be checked, and its markers
41
+ * count as written — what the index did before it read pre-images at all.
42
+ *
43
+ * The pre-image answers one more thing the delta does not spell: a
44
+ * CHANNEL-slot removal retires the messages its living channel declared
45
+ * inline, and those names exist only in the contract the merge ran against. A
46
+ * feature archived without a pre-image contributes its channel slot and not
47
+ * the inline names.
48
+ *
49
+ * What the index records is every ADDRESS an archived feature vacated, per
50
+ * operationId or message name: where each executed marker stood, whether the
51
+ * relocation rule calls it a retirement or a move. A relocation retires
52
+ * nothing, but it does vacate an address: the id lives on at its new slot,
53
+ * and a later delta quoting the old one would put it back there too — one
54
+ * operationId at two addresses, which the merge cannot tell from an addition
55
+ * any more than it can a retired operation, and which the slot-keyed
56
+ * baseline gate and an id-keyed check both miss (the id is living, the slot
57
+ * is empty). A retirement vacates its address the same way once a later
58
+ * feature re-adds the id somewhere else: indexed by id alone, as this once
59
+ * was, a quote of the retired address read as harmless because the id was
60
+ * living, and archived the id at both. So a vacation carries its address,
61
+ * and for a move where the delta put the entry — kept, because where it
62
+ * lives NOW may be somewhere a later feature put it (`Vacated`). The event
63
+ * axis vacates message addresses the same two ways: a `components.messages`
64
+ * marker, and a channel removal taking its inline declarations. A channel or
65
+ * operation slot's identity IS its key, so a "moved" channel is a retired key
66
+ * plus a new one, and the slot index catches a quote of the old key.
67
+ *
68
+ * What it cannot see, and SCHEMA.md says so: a removal made by hand-editing a
69
+ * living contract leaves no marker anywhere, and an archived feature deleted
70
+ * from disk takes its markers with it. Both read as "never retired" — the gate
71
+ * falls back to calling the entry new, which is what it did before.
72
+ *
73
+ * OpenAPI markers count only when they name an operationId. A marker with
74
+ * none (`openapi.remove-marker-anonymous`) or one written at PATH level
75
+ * (`openapi.remove-marker-path-level`) retires nothing at archive — the merge
76
+ * matches a marker to the living operation by the operationId it names, at
77
+ * its own method and path, and these name none — so an archived feature that
78
+ * carried one under `--approve` left the operation living, and indexing its
79
+ * slot would attribute a retirement that never happened.
80
+ */
81
+ import { existsSync, statSync } from "node:fs";
82
+ import { resolve } from "node:path";
83
+ import { parse } from "yaml";
84
+ import { inOrder, memo } from "../../kernel/concurrency.js";
85
+ import { archiveDir, ARTIFACT_FILES, featureSpecPaths } from "../../repo/paths.js";
86
+ import { listFeatures } from "../../repo/repo.js";
87
+ import { openapiFromText, readOpenapi } from "../../openapi/doc.js";
88
+ import { readOr } from "../../openapi/contract-text.js";
89
+ import { asyncapiSlots, slotKey, slotsByKey } from "../../asyncapi/digest.js";
90
+ import { asyncapiFromText, readAsyncapi } from "../../asyncapi/read.js";
91
+ import { readSnapshotPreImage } from "../../staging/snapshot/manifest.js";
92
+ import { messageRetirements, operationRetirements } from "./retirement.js";
93
+ /** `METHOD /path` — how the index records, and every finding names, an operation's address. */
94
+ export function operationAddress(op) {
95
+ return `${op.method.toUpperCase()} ${op.path}`;
96
+ }
97
+ /** The index's one key spelling. NUL-joined: no id, section, key or message name can contain it. */
98
+ export function retiredKey(...parts) {
99
+ return parts.join("\0");
100
+ }
101
+ /**
102
+ * One index per invocation, keyed by the invocation's own FleetContext.
103
+ *
104
+ * A module-level cache is normally the wrong shape (AGENTS.md: a value cached
105
+ * at import time leaks across tests and, in a long-running host, across
106
+ * invocations). This one is the `c4/resolve/service.ts` exception for the same
107
+ * reason: the key is the per-invocation context object, so two runs can never
108
+ * see each other's answer and the entry dies with the context. It is here
109
+ * rather than a memo on FleetContext because that class sits at L6 and this
110
+ * package at L8 — the import that memo would need points the wrong way up the
111
+ * DAG (docs/DESIGN.md's level table) — while the reads underneath (the feature
112
+ * list, every contract parse) already go through the context's own memos.
113
+ */
114
+ const PER_INVOCATION = new WeakMap();
115
+ /**
116
+ * A lazy handle on the index: nothing is read until a grader has a candidate
117
+ * to ask about — an entry the merged contract answers at an address its
118
+ * living contract does not, which is exactly the shape the living contract
119
+ * alone cannot call new or revived. A feature that only edits what the living
120
+ * contract already holds never walks `features/archive/`; one that adds
121
+ * anything genuinely new does, since an addition and a revival read the same
122
+ * until the walk answers. Without a context (archive's gate) the handle
123
+ * memoises for its own lifetime, which is one `featureCoherence` call, so the
124
+ * two contract axes still share one walk.
125
+ */
126
+ export function retiredLookup(docsDir, context) {
127
+ let own;
128
+ return () => {
129
+ if (context === undefined)
130
+ return (own ??= readRetiredIndex(docsDir));
131
+ let store = PER_INVOCATION.get(context);
132
+ if (store === undefined)
133
+ PER_INVOCATION.set(context, (store = new Map()));
134
+ return memo(store, resolve(docsDir), () => readRetiredIndex(docsDir, context));
135
+ };
136
+ }
137
+ /** The index's parts, in the one order `readRetiredIndex` records them. */
138
+ const PARTS = ["operations", "slots", "messages"];
139
+ /** Walk every archived feature's contract deltas and record every address each one vacated. */
140
+ async function readRetiredIndex(docsDir, context) {
141
+ const index = {
142
+ operations: new Map(),
143
+ slots: new Map(),
144
+ messages: new Map(),
145
+ };
146
+ // A `features/archive` that is not a directory holds no archived feature,
147
+ // and listing it would throw ENOTDIR out of the feature gate — refusing an
148
+ // archive over a question about OTHER features' history, before the plan
149
+ // that names the real problem (the final move into that path) is reached.
150
+ if (!isDirectory(archiveDir(docsDir)))
151
+ return index;
152
+ const archived = (await listFeatures(docsDir, { includeArchived: true }, context)).filter((f) => f.archived);
153
+ const units = archived.flatMap((feature) => feature.services.map((service) => ({ feature, service })));
154
+ // Every (feature, service) pair is independent of every other, so they are
155
+ // read concurrently; the only order that matters is the order retirers are
156
+ // RECORDED in, and `inOrder` hands the answers back in input order — the
157
+ // features sorted by `compareIds`, as `listFeatures` returns them.
158
+ for (const found of await inOrder(units, ({ feature, service }) => serviceRetirements(feature, service, context))) {
159
+ for (const part of PARTS) {
160
+ for (const { key, at, to } of found[part])
161
+ record(index[part], key, { by: found.featureId, at, to });
162
+ }
163
+ }
164
+ return index;
165
+ }
166
+ /** One archived feature's vacations on one service — only the markers its merge carried out. */
167
+ async function serviceRetirements(feature, service, context) {
168
+ const found = { featureId: feature.id, operations: [], slots: [], messages: [] };
169
+ const paths = featureSpecPaths(feature.dir, service);
170
+ // An absent delta reads as an empty document; an unreadable one retired
171
+ // nothing loam could have merged (archive refuses `merge-failed`).
172
+ const api = await readOpenapi(paths.openapi, context);
173
+ if (!api.unreadable && api.ops.some((op) => op.remove)) {
174
+ const before = await preImage(feature.dir, service, ARTIFACT_FILES.openapi);
175
+ const held = before === undefined ? undefined : openapiFromText(before);
176
+ // The merge's own match: the operation the pre-image held AT the marker's
177
+ // slot carries the marker's id. Markers that missed are dropped before the
178
+ // rule runs, so a relocation whose old-slot marker missed is no
179
+ // relocation either — it moved nothing out.
180
+ const carried = api.ops.filter((op) => !op.remove ||
181
+ held === undefined ||
182
+ held.unreadable ||
183
+ held.ops.some((h) => !h.remove && h.id === op.id && h.method === op.method && h.path === op.path));
184
+ // Every carried marker stands on an address it vacated. The relocation
185
+ // rule says which kind: an id in `net` is retired, and one the rule took
186
+ // out of `net` moved — the delta's definitions of it are where it went.
187
+ const { net } = operationRetirements(carried);
188
+ for (const op of carried) {
189
+ if (!op.remove)
190
+ continue;
191
+ const to = net.has(op.id) ? [] : carried.filter((d) => !d.remove && d.id === op.id).map(operationAddress);
192
+ found.operations.push({ key: retiredKey(service, op.id), at: operationAddress(op), to });
193
+ }
194
+ }
195
+ if (!existsSync(paths.asyncapi))
196
+ return found;
197
+ const events = await readAsyncapi(paths.asyncapi, context);
198
+ const text = events.unreadable ? undefined : await readOr(paths.asyncapi, context);
199
+ if (text === undefined)
200
+ return found;
201
+ const eventSlots = asyncapiSlots(parse(text));
202
+ if (!eventSlots.some((s) => s.remove))
203
+ return found;
204
+ const before = await preImageEvents(feature.dir, service);
205
+ // Presence is the whole match for a channel or operation slot — its
206
+ // identity IS the key. A message marker must also name the message the
207
+ // pre-image declared at that key, the merge's exactness on the message axis.
208
+ const carriedSlots = eventSlots.filter((s) => !s.remove || before === undefined || before.slots.has(slotKey(s)));
209
+ const carriedEvents = {
210
+ ...events,
211
+ messages: events.messages.filter((m) => m.remove !== true ||
212
+ before === undefined ||
213
+ before.doc.messages.some((b) => b.remove !== true && b.slot === m.slot && b.name === m.name)),
214
+ };
215
+ for (const slot of carriedSlots) {
216
+ if (!slot.remove || slot.section === "components.messages")
217
+ continue;
218
+ found.slots.push({ key: retiredKey(service, slot.section, slot.key), at: `${slot.section}.${slot.key}`, to: [] });
219
+ }
220
+ // The operation axis's rule on the message name: a vacated declaration
221
+ // whose name the delta declares again was relocated — to another key, or
222
+ // inline under a channel — and those declarations are where it went.
223
+ const retired = messageRetirements(carriedEvents, carriedSlots, before?.doc ?? NOTHING_DECLARED);
224
+ for (const m of retired.vacated) {
225
+ const to = retired.net.has(m.name)
226
+ ? []
227
+ : carriedEvents.messages.filter((d) => d.remove !== true && d.name === m.name).map((d) => d.slot);
228
+ found.messages.push({ key: retiredKey(service, m.name), at: m.slot, to });
229
+ }
230
+ return found;
231
+ }
232
+ const NOTHING_DECLARED = asyncapiFromText("");
233
+ /**
234
+ * The contract one artifact held as this feature's merge found it, or
235
+ * undefined when the snapshot holds nothing to trust. An artifact the merge
236
+ * created, or never wrote, lost nothing to it — which is exactly what an empty
237
+ * pre-image says: every marker in the delta missed, and no channel took inline
238
+ * names with it.
239
+ */
240
+ async function preImage(featureDir, service, artifact) {
241
+ const found = await readSnapshotPreImage(featureDir, { service, artifact });
242
+ if (found === null)
243
+ return undefined;
244
+ return found.kind === "held" ? found.text : "";
245
+ }
246
+ /** The event pre-image as the reader and the slot walk see it — undefined when absent or unreadable. */
247
+ async function preImageEvents(featureDir, service) {
248
+ const text = await preImage(featureDir, service, ARTIFACT_FILES.asyncapi);
249
+ if (text === undefined)
250
+ return undefined;
251
+ const doc = asyncapiFromText(text);
252
+ return doc.unreadable ? undefined : { doc, slots: slotsByKey(asyncapiSlots(parse(text))) };
253
+ }
254
+ function isDirectory(path) {
255
+ try {
256
+ return statSync(path).isDirectory();
257
+ }
258
+ catch {
259
+ return false;
260
+ }
261
+ }
262
+ /**
263
+ * Add one vacation. Already in id order without a sort: the walk records the
264
+ * features in `listFeatures`' `compareIds` order. Distinct per feature,
265
+ * address and kind, because two archived directories can carry one feature
266
+ * id (the id is read off the directory name), and "archived FEAT-7 and
267
+ * FEAT-7" names one feature twice — a second directory's move away from the
268
+ * same address widens that feature's destinations instead.
269
+ */
270
+ function record(into, key, vacated) {
271
+ const all = into.get(key);
272
+ if (all === undefined) {
273
+ into.set(key, [vacated]);
274
+ return;
275
+ }
276
+ const same = (v) => v.by === vacated.by && v.at === vacated.at && (v.to.length === 0) === (vacated.to.length === 0);
277
+ if (!all.some(same)) {
278
+ into.set(key, [...all, vacated]);
279
+ return;
280
+ }
281
+ const widen = (v) => (same(v) ? { ...v, to: [...new Set([...v.to, ...vacated.to])] } : v);
282
+ const [first, ...rest] = all;
283
+ into.set(key, [widen(first), ...rest.map(widen)]);
284
+ }
285
+ //# sourceMappingURL=archived.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archived.js","sourceRoot":"","sources":["../../../../src/core/coherence/retired/archived.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The retired index: what every ARCHIVED feature took out of the fleet's living\n * contracts, and who took it.\n *\n * The question it answers is one the living contract cannot: is an operation\n * the living contract does not have NEW, or was it RETIRED? Both read as\n * absent. Before this index existed the feature gate could only treat absent\n * as new — so a delta quoting an operation that a parallel feature retired and\n * archived first was graded as an addition (`api.op-unconsumed`, advisory) and\n * archived at exit 0, putting the retired operation straight back into the\n * living contract. The baseline gate's unpinned count (openapi/baseline/gate.ts)\n * could not see it either: it counts only quotes of operations the living\n * contract still has, so that a genuinely new operation never trips it.\n *\n * The record of a retirement is files, not history. An archived feature keeps\n * its delta under `features/archive/<FEAT>/specs/<svc>/`, and the removal\n * markers in it say what that merge was ASKED to delete — read through the\n * same rule the gate grades an in-flight delta by (./retirement.ts), so\n * relocations are not retirements here either. No git: the docs semantics stay\n * a property of the tree.\n *\n * Asked is not done. The OpenAPI merge deletes only when the living operation\n * AT the marker's method and path carries the marker's operationId\n * (openapi/merge/merge.ts); the event merge deletes a channel or operation slot\n * only when the living contract has that key, and a `components.messages` slot\n * only when the message declared there carries the marker's name\n * (asyncapi/merge/merge.ts). A marker that missed — `remove-target-missing` or\n * `remove-target-mismatch`, archived past under `--approve` — deleted nothing,\n * and counting it called a genuinely new operation a revival and refused it.\n * So every marker is checked against the contract the merge ran against: the\n * feature's snapshot pre-image (`.loam-before/`, staging/snapshot/manifest.ts).\n * A contract the archive CREATED, or never wrote at all, lost nothing to that\n * merge, and reads as an empty pre-image. \"Never wrote\" is the event merge's\n * case alone: it returns no text when it changes nothing (an asyncapi delta\n * whose every marker missed), and archive snapshots only what it writes. The\n * OpenAPI merge returns text whenever the delta has a path entry, so a\n * contract it left unchanged has an ordinary pre-image equal to what it\n * wrote — read like any other, and every missed marker misses it again. A\n * feature with no intact snapshot (archived before the snapshot existed, or\n * the snapshot edited or removed since) cannot be checked, and its markers\n * count as written — what the index did before it read pre-images at all.\n *\n * The pre-image answers one more thing the delta does not spell: a\n * CHANNEL-slot removal retires the messages its living channel declared\n * inline, and those names exist only in the contract the merge ran against. A\n * feature archived without a pre-image contributes its channel slot and not\n * the inline names.\n *\n * What the index records is every ADDRESS an archived feature vacated, per\n * operationId or message name: where each executed marker stood, whether the\n * relocation rule calls it a retirement or a move. A relocation retires\n * nothing, but it does vacate an address: the id lives on at its new slot,\n * and a later delta quoting the old one would put it back there too — one\n * operationId at two addresses, which the merge cannot tell from an addition\n * any more than it can a retired operation, and which the slot-keyed\n * baseline gate and an id-keyed check both miss (the id is living, the slot\n * is empty). A retirement vacates its address the same way once a later\n * feature re-adds the id somewhere else: indexed by id alone, as this once\n * was, a quote of the retired address read as harmless because the id was\n * living, and archived the id at both. So a vacation carries its address,\n * and for a move where the delta put the entry — kept, because where it\n * lives NOW may be somewhere a later feature put it (`Vacated`). The event\n * axis vacates message addresses the same two ways: a `components.messages`\n * marker, and a channel removal taking its inline declarations. A channel or\n * operation slot's identity IS its key, so a \"moved\" channel is a retired key\n * plus a new one, and the slot index catches a quote of the old key.\n *\n * What it cannot see, and SCHEMA.md says so: a removal made by hand-editing a\n * living contract leaves no marker anywhere, and an archived feature deleted\n * from disk takes its markers with it. Both read as \"never retired\" — the gate\n * falls back to calling the entry new, which is what it did before.\n *\n * OpenAPI markers count only when they name an operationId. A marker with\n * none (`openapi.remove-marker-anonymous`) or one written at PATH level\n * (`openapi.remove-marker-path-level`) retires nothing at archive — the merge\n * matches a marker to the living operation by the operationId it names, at\n * its own method and path, and these name none — so an archived feature that\n * carried one under `--approve` left the operation living, and indexing its\n * slot would attribute a retirement that never happened.\n */\nimport { existsSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { parse } from \"yaml\";\nimport { inOrder, memo } from \"../../kernel/concurrency.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\nimport type { RawServiceId } from \"../../kernel/ids/service.js\";\nimport type { FeatureEntry } from \"../../repo/entries.js\";\nimport { archiveDir, ARTIFACT_FILES, featureSpecPaths } from \"../../repo/paths.js\";\nimport { listFeatures } from \"../../repo/repo.js\";\nimport { openapiFromText, readOpenapi } from \"../../openapi/doc.js\";\nimport { readOr } from \"../../openapi/contract-text.js\";\nimport { asyncapiSlots, slotKey, slotsByKey, type AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport { asyncapiFromText, readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport { readSnapshotPreImage } from \"../../staging/snapshot/manifest.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport { messageRetirements, operationRetirements } from \"./retirement.js\";\n\n/**\n * One archived feature taking one entry out of one address: a retirement\n * (`to` empty) or a move (`to`, every address its delta put the entry at).\n * A move's destination is recorded apart from where the entry lives NOW,\n * because the two part ways as soon as anything lands after the move — A\n * moves an operation from /x to /y, B retires it at /y, D re-adds it at /z —\n * and a finding that named the living address as the mover's destination\n * said \"A moved it to /z\", an address A never wrote.\n */\nexport interface Vacated {\n by: string;\n /** `METHOD /path` for an operation, the slot for a message, `<section>.<key>` for a channel or operation slot. */\n at: string;\n to: readonly string[];\n}\n\n/**\n * Every vacation of one entry, in feature id order and distinct per feature,\n * address and kind — never empty, because an entry is recorded only together\n * with its first vacation. The tuple says so to the compiler, so the one\n * sentence that names the features (./revival/revived.ts) cannot be handed nobody.\n */\nexport type Vacations = readonly [Vacated, ...Vacated[]];\n\n/** `METHOD /path` — how the index records, and every finding names, an operation's address. */\nexport function operationAddress(op: { method: string; path: string }): string {\n return `${op.method.toUpperCase()} ${op.path}`;\n}\n\n/** Everything the archived features retired or moved away, each entry naming the features that did it and where. */\nexport interface RetiredIndex {\n /** `retiredKey(service, operationId)` → every address an archived openapi delta took it out of. */\n operations: ReadonlyMap<string, Vacations>;\n /** `retiredKey(service, section, key)` for a `channels` or `operations` slot — always a retirement. */\n slots: ReadonlyMap<string, Vacations>;\n /** `retiredKey(service, messageName)` — by the join token, every slot it was taken out of. */\n messages: ReadonlyMap<string, Vacations>;\n}\n\n/** The index's one key spelling. NUL-joined: no id, section, key or message name can contain it. */\nexport function retiredKey(...parts: string[]): string {\n return parts.join(\"\\0\");\n}\n\n/** The index, built at most once per caller — what the gate hands both contract axes. */\nexport type RetiredLookup = () => Promise<RetiredIndex>;\n\n/**\n * One index per invocation, keyed by the invocation's own FleetContext.\n *\n * A module-level cache is normally the wrong shape (AGENTS.md: a value cached\n * at import time leaks across tests and, in a long-running host, across\n * invocations). This one is the `c4/resolve/service.ts` exception for the same\n * reason: the key is the per-invocation context object, so two runs can never\n * see each other's answer and the entry dies with the context. It is here\n * rather than a memo on FleetContext because that class sits at L6 and this\n * package at L8 — the import that memo would need points the wrong way up the\n * DAG (docs/DESIGN.md's level table) — while the reads underneath (the feature\n * list, every contract parse) already go through the context's own memos.\n */\nconst PER_INVOCATION = new WeakMap<FleetContext, Map<string, Promise<RetiredIndex>>>();\n\n/**\n * A lazy handle on the index: nothing is read until a grader has a candidate\n * to ask about — an entry the merged contract answers at an address its\n * living contract does not, which is exactly the shape the living contract\n * alone cannot call new or revived. A feature that only edits what the living\n * contract already holds never walks `features/archive/`; one that adds\n * anything genuinely new does, since an addition and a revival read the same\n * until the walk answers. Without a context (archive's gate) the handle\n * memoises for its own lifetime, which is one `featureCoherence` call, so the\n * two contract axes still share one walk.\n */\nexport function retiredLookup(docsDir: DocsDir, context?: FleetContext): RetiredLookup {\n let own: Promise<RetiredIndex> | undefined;\n return () => {\n if (context === undefined) return (own ??= readRetiredIndex(docsDir));\n let store = PER_INVOCATION.get(context);\n if (store === undefined) PER_INVOCATION.set(context, (store = new Map()));\n return memo(store, resolve(docsDir), () => readRetiredIndex(docsDir, context));\n };\n}\n\n/** One vacation, as one archived feature's walk finds it: the index key of the entry, the address, and any destinations. */\ninterface Found {\n key: string;\n at: string;\n to: string[];\n}\n\n/** What one archived feature's deltas for one service took out, per index part. */\ninterface ServiceRetirements {\n featureId: string;\n operations: Found[];\n slots: Found[];\n messages: Found[];\n}\n\n/** The index's parts, in the one order `readRetiredIndex` records them. */\nconst PARTS = [\"operations\", \"slots\", \"messages\"] as const;\n\n/** Walk every archived feature's contract deltas and record every address each one vacated. */\nasync function readRetiredIndex(docsDir: DocsDir, context?: FleetContext): Promise<RetiredIndex> {\n const index = {\n operations: new Map<string, Vacations>(),\n slots: new Map<string, Vacations>(),\n messages: new Map<string, Vacations>(),\n };\n // A `features/archive` that is not a directory holds no archived feature,\n // and listing it would throw ENOTDIR out of the feature gate — refusing an\n // archive over a question about OTHER features' history, before the plan\n // that names the real problem (the final move into that path) is reached.\n if (!isDirectory(archiveDir(docsDir))) return index;\n const archived = (await listFeatures(docsDir, { includeArchived: true }, context)).filter((f) => f.archived);\n const units = archived.flatMap((feature) => feature.services.map((service) => ({ feature, service })));\n // Every (feature, service) pair is independent of every other, so they are\n // read concurrently; the only order that matters is the order retirers are\n // RECORDED in, and `inOrder` hands the answers back in input order — the\n // features sorted by `compareIds`, as `listFeatures` returns them.\n for (const found of await inOrder(units, ({ feature, service }) => serviceRetirements(feature, service, context))) {\n for (const part of PARTS) {\n for (const { key, at, to } of found[part]) record(index[part], key, { by: found.featureId, at, to });\n }\n }\n return index;\n}\n\n/** One archived feature's vacations on one service — only the markers its merge carried out. */\nasync function serviceRetirements(\n feature: FeatureEntry,\n service: RawServiceId,\n context: FleetContext | undefined,\n): Promise<ServiceRetirements> {\n const found: ServiceRetirements = { featureId: feature.id, operations: [], slots: [], messages: [] };\n const paths = featureSpecPaths(feature.dir, service);\n // An absent delta reads as an empty document; an unreadable one retired\n // nothing loam could have merged (archive refuses `merge-failed`).\n const api = await readOpenapi(paths.openapi, context);\n if (!api.unreadable && api.ops.some((op) => op.remove)) {\n const before = await preImage(feature.dir, service, ARTIFACT_FILES.openapi);\n const held = before === undefined ? undefined : openapiFromText(before);\n // The merge's own match: the operation the pre-image held AT the marker's\n // slot carries the marker's id. Markers that missed are dropped before the\n // rule runs, so a relocation whose old-slot marker missed is no\n // relocation either — it moved nothing out.\n const carried = api.ops.filter(\n (op) =>\n !op.remove ||\n held === undefined ||\n held.unreadable ||\n held.ops.some((h) => !h.remove && h.id === op.id && h.method === op.method && h.path === op.path),\n );\n // Every carried marker stands on an address it vacated. The relocation\n // rule says which kind: an id in `net` is retired, and one the rule took\n // out of `net` moved — the delta's definitions of it are where it went.\n const { net } = operationRetirements(carried);\n for (const op of carried) {\n if (!op.remove) continue;\n const to = net.has(op.id) ? [] : carried.filter((d) => !d.remove && d.id === op.id).map(operationAddress);\n found.operations.push({ key: retiredKey(service, op.id), at: operationAddress(op), to });\n }\n }\n if (!existsSync(paths.asyncapi)) return found;\n const events = await readAsyncapi(paths.asyncapi, context);\n const text = events.unreadable ? undefined : await readOr(paths.asyncapi, context);\n if (text === undefined) return found;\n const eventSlots = asyncapiSlots(parse(text));\n if (!eventSlots.some((s) => s.remove)) return found;\n const before = await preImageEvents(feature.dir, service);\n // Presence is the whole match for a channel or operation slot — its\n // identity IS the key. A message marker must also name the message the\n // pre-image declared at that key, the merge's exactness on the message axis.\n const carriedSlots = eventSlots.filter((s) => !s.remove || before === undefined || before.slots.has(slotKey(s)));\n const carriedEvents: AsyncapiDoc = {\n ...events,\n messages: events.messages.filter(\n (m) =>\n m.remove !== true ||\n before === undefined ||\n before.doc.messages.some((b) => b.remove !== true && b.slot === m.slot && b.name === m.name),\n ),\n };\n for (const slot of carriedSlots) {\n if (!slot.remove || slot.section === \"components.messages\") continue;\n found.slots.push({ key: retiredKey(service, slot.section, slot.key), at: `${slot.section}.${slot.key}`, to: [] });\n }\n // The operation axis's rule on the message name: a vacated declaration\n // whose name the delta declares again was relocated — to another key, or\n // inline under a channel — and those declarations are where it went.\n const retired = messageRetirements(carriedEvents, carriedSlots, before?.doc ?? NOTHING_DECLARED);\n for (const m of retired.vacated) {\n const to = retired.net.has(m.name)\n ? []\n : carriedEvents.messages.filter((d) => d.remove !== true && d.name === m.name).map((d) => d.slot);\n found.messages.push({ key: retiredKey(service, m.name), at: m.slot, to });\n }\n return found;\n}\n\nconst NOTHING_DECLARED: AsyncapiDoc = asyncapiFromText(\"\");\n\n/**\n * The contract one artifact held as this feature's merge found it, or\n * undefined when the snapshot holds nothing to trust. An artifact the merge\n * created, or never wrote, lost nothing to it — which is exactly what an empty\n * pre-image says: every marker in the delta missed, and no channel took inline\n * names with it.\n */\nasync function preImage(featureDir: string, service: string, artifact: string): Promise<string | undefined> {\n const found = await readSnapshotPreImage(featureDir, { service, artifact });\n if (found === null) return undefined;\n return found.kind === \"held\" ? found.text : \"\";\n}\n\n/** The event pre-image as the reader and the slot walk see it — undefined when absent or unreadable. */\nasync function preImageEvents(\n featureDir: string,\n service: string,\n): Promise<{ doc: AsyncapiDoc; slots: ReadonlyMap<string, AsyncapiSlot> } | undefined> {\n const text = await preImage(featureDir, service, ARTIFACT_FILES.asyncapi);\n if (text === undefined) return undefined;\n const doc = asyncapiFromText(text);\n return doc.unreadable ? undefined : { doc, slots: slotsByKey(asyncapiSlots(parse(text))) };\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Add one vacation. Already in id order without a sort: the walk records the\n * features in `listFeatures`' `compareIds` order. Distinct per feature,\n * address and kind, because two archived directories can carry one feature\n * id (the id is read off the directory name), and \"archived FEAT-7 and\n * FEAT-7\" names one feature twice — a second directory's move away from the\n * same address widens that feature's destinations instead.\n */\nfunction record(into: Map<string, Vacations>, key: string, vacated: Vacated): void {\n const all = into.get(key);\n if (all === undefined) {\n into.set(key, [vacated]);\n return;\n }\n const same = (v: Vacated): boolean =>\n v.by === vacated.by && v.at === vacated.at && (v.to.length === 0) === (vacated.to.length === 0);\n if (!all.some(same)) {\n into.set(key, [...all, vacated]);\n return;\n }\n const widen = (v: Vacated): Vacated => (same(v) ? { ...v, to: [...new Set([...v.to, ...vacated.to])] } : v);\n const [first, ...rest] = all;\n into.set(key, [widen(first), ...rest.map(widen)]);\n}\n"]}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * One service's pair of event contracts — the feature delta and the living
3
+ * contract — read the one way every slot judgement on the event axis reads
4
+ * them.
5
+ *
6
+ * Two readers build it, and they must never disagree: the feature gate's walk
7
+ * (../events/declared.ts), which grades baselines and markers and collects the
8
+ * revival candidates, and `serviceRevivals` (./revival/revived.ts), which asks the
9
+ * revival question standalone for `loam rebase`. The second used to re-spell
10
+ * the first's guards — existence, decode, an unreadable living side — and its
11
+ * own living-slot map, so a change to the gate's guard would have left rebase
12
+ * and validate giving two answers about one tree with no test comparing them.
13
+ *
14
+ * Here, in retired/, because it is the lowest package both readers import: the
15
+ * event walk already imports this package (for the revival candidates), and a
16
+ * value edge from here up into events/ would be a package cycle. The merge
17
+ * simulation (./merged.ts) is here for the same reason, and rides on the pair
18
+ * as `merged()`: the walk grades the post-archive contract with it, and the
19
+ * revival check compares it with the living contract — one simulation per
20
+ * pair, whichever asks first.
21
+ */
22
+ import { existsSync } from "node:fs";
23
+ import { parse } from "yaml";
24
+ import { readOr } from "../../openapi/contract-text.js";
25
+ import { asyncapiSlots, slotsByKey } from "../../asyncapi/digest.js";
26
+ import { mergedAsyncapiDoc } from "./merged.js";
27
+ /**
28
+ * Read both sides' texts and slots. Each text is parsed ONCE and kept: the
29
+ * slot walk and the surface half of the baseline grade read the same resolved
30
+ * tree, and a second `parse` of the same bytes is a second chance for the two
31
+ * halves to disagree about what the delta declares.
32
+ */
33
+ export async function readEventPair(sides, context) {
34
+ const { featPath, featDoc, livingPath, livingDoc, service } = sides;
35
+ const featText = sides.featText ?? (await readOr(featPath, context));
36
+ const featPlain = featText === undefined ? {} : parse(featText);
37
+ const livingExists = existsSync(livingPath);
38
+ const livingText = !livingExists || livingDoc.unreadable ? undefined : await readOr(livingPath, context);
39
+ const livingPlain = livingText === undefined ? {} : parse(livingText);
40
+ const livingAnswers = livingText !== undefined || !livingExists;
41
+ // Lazy: most pairs never need it on the rebase path, and the walk asks for
42
+ // it once. Boxed, so an `undefined` answer is remembered too.
43
+ let simulated;
44
+ const merged = () => (simulated ??= {
45
+ merged: !livingAnswers || featText === undefined
46
+ ? undefined
47
+ : mergedAsyncapiDoc({ livingText, livingDoc, livingRoot: livingPlain, featureText: featText, service }),
48
+ }).merged;
49
+ return {
50
+ featDoc,
51
+ featSlots: asyncapiSlots(featPlain),
52
+ featPlain,
53
+ featText,
54
+ livingDoc,
55
+ livingSlots: slotsByKey(asyncapiSlots(livingPlain)),
56
+ livingText,
57
+ livingPlain,
58
+ livingAnswers,
59
+ merged,
60
+ };
61
+ }
62
+ //# sourceMappingURL=event-pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-pair.js","sourceRoot":"","sources":["../../../../src/core/coherence/retired/event-pair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAqB,MAAM,0BAA0B,CAAC;AAGxF,OAAO,EAAE,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AAiEnE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAgB,EAAE,OAAsB;IAC1E,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACpE,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACrE,MAAM,SAAS,GAAY,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzE,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,CAAC,YAAY,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACzG,MAAM,WAAW,GAAY,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/E,MAAM,aAAa,GAAG,UAAU,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC;IAChE,2EAA2E;IAC3E,8DAA8D;IAC9D,IAAI,SAA2D,CAAC;IAChE,MAAM,MAAM,GAAG,GAA6B,EAAE,CAC5C,CAAC,SAAS,KAAK;QACb,MAAM,EAAE,CAAC,aAAa,IAAI,QAAQ,KAAK,SAAS;YAC9C,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,iBAAiB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;KAC1G,CAAC,CAAC,MAAM,CAAC;IACZ,OAAO;QACL,OAAO;QACP,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC;QACnC,SAAS;QACT,QAAQ;QACR,SAAS;QACT,WAAW,EAAE,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QACnD,UAAU;QACV,WAAW;QACX,aAAa;QACb,MAAM;KACP,CAAC;AACJ,CAAC","sourcesContent":["/**\n * One service's pair of event contracts — the feature delta and the living\n * contract — read the one way every slot judgement on the event axis reads\n * them.\n *\n * Two readers build it, and they must never disagree: the feature gate's walk\n * (../events/declared.ts), which grades baselines and markers and collects the\n * revival candidates, and `serviceRevivals` (./revival/revived.ts), which asks the\n * revival question standalone for `loam rebase`. The second used to re-spell\n * the first's guards — existence, decode, an unreadable living side — and its\n * own living-slot map, so a change to the gate's guard would have left rebase\n * and validate giving two answers about one tree with no test comparing them.\n *\n * Here, in retired/, because it is the lowest package both readers import: the\n * event walk already imports this package (for the revival candidates), and a\n * value edge from here up into events/ would be a package cycle. The merge\n * simulation (./merged.ts) is here for the same reason, and rides on the pair\n * as `merged()`: the walk grades the post-archive contract with it, and the\n * revival check compares it with the living contract — one simulation per\n * pair, whichever asks first.\n */\nimport { existsSync } from \"node:fs\";\nimport { parse } from \"yaml\";\nimport { readOr } from \"../../openapi/contract-text.js\";\nimport { asyncapiSlots, slotsByKey, type AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport { mergedAsyncapiDoc, type MergedEvents } from \"./merged.js\";\n\n/** The four facts every slot judgement reads: each side as the reader parsed it, and as its slots. */\nexport interface EventPair {\n featDoc: AsyncapiDoc;\n featSlots: AsyncapiSlot[];\n livingDoc: AsyncapiDoc;\n /** The living contract's slots by `slotKey`; empty where it is absent or did not read. */\n livingSlots: Map<string, AsyncapiSlot>;\n}\n\n/** The pair, plus the texts and trees the gate's other graders read from the same bytes. */\nexport interface EventPairRead extends EventPair {\n /** The feature delta's resolved parse — `{}` when its text did not decode. */\n featPlain: unknown;\n featText: string | undefined;\n /** The living contract's text — undefined when it is ABSENT or did not read; `livingAnswers` tells the two apart. */\n livingText: string | undefined;\n /** The living contract's resolved parse; `{}` where there is no living text. */\n livingPlain: unknown;\n /**\n * Can anything be graded against the living side? Absent is a real answer —\n * everything the delta spells is new, and a baseline only means something\n * for a slot that already exists. Unreadable is NOT an answer: grading pins,\n * markers or revivals against an empty parse would call every restatement\n * new and every marker stale, so each of those checks stands down and the\n * (later) merge refuses the unreadable side by name.\n */\n livingAnswers: boolean;\n /**\n * The living contract as the archive would leave it (./merged.ts), computed\n * on first ask and kept — the create branch's document where no living\n * contract exists; undefined where the living side does not answer, or the\n * merge refuses the pair. The walk asks it only with a living text in hand.\n * Its tree rides along for the one reader that follows a `$ref` back.\n */\n merged(): MergedEvents | undefined;\n}\n\n/**\n * Where one service's two contracts of one axis live, and their parses — read\n * by the caller, which needs them before this. The OpenAPI revival read\n * (./revival/arrivals.ts) takes the same shape.\n */\nexport interface PairSides<Doc = AsyncapiDoc> {\n featPath: string;\n /** Readable: an unreadable feature delta is `asyncapi.invalid` (`openapi.invalid`), and nothing is graded against it. */\n featDoc: Doc;\n livingPath: string;\n livingDoc: Doc;\n /** The service the pair belongs to — what a refusal of the simulated merge would name. */\n service: string;\n /**\n * The delta's text when the caller holds a version of it that is not on\n * disk yet — `loam rebase`'s, with the pins it is about to write; read from\n * `featPath` when absent. The merge's quote/edit verdict turns on the pins,\n * so the revival question asked of the delta as it stood before a repin can\n * answer for a document rebase is about to replace: a quote pinned to its\n * own content is skipped, and repinned over another operation it is an edit\n * that lands. A pin changes no identity or address, so `featDoc`, read from\n * disk, still names what the text declares.\n */\n featText?: string;\n}\n\n/**\n * Read both sides' texts and slots. Each text is parsed ONCE and kept: the\n * slot walk and the surface half of the baseline grade read the same resolved\n * tree, and a second `parse` of the same bytes is a second chance for the two\n * halves to disagree about what the delta declares.\n */\nexport async function readEventPair(sides: PairSides, context?: FleetContext): Promise<EventPairRead> {\n const { featPath, featDoc, livingPath, livingDoc, service } = sides;\n const featText = sides.featText ?? (await readOr(featPath, context));\n const featPlain: unknown = featText === undefined ? {} : parse(featText);\n const livingExists = existsSync(livingPath);\n const livingText = !livingExists || livingDoc.unreadable ? undefined : await readOr(livingPath, context);\n const livingPlain: unknown = livingText === undefined ? {} : parse(livingText);\n const livingAnswers = livingText !== undefined || !livingExists;\n // Lazy: most pairs never need it on the rebase path, and the walk asks for\n // it once. Boxed, so an `undefined` answer is remembered too.\n let simulated: { merged: MergedEvents | undefined } | undefined;\n const merged = (): MergedEvents | undefined =>\n (simulated ??= {\n merged: !livingAnswers || featText === undefined\n ? undefined\n : mergedAsyncapiDoc({ livingText, livingDoc, livingRoot: livingPlain, featureText: featText, service }),\n }).merged;\n return {\n featDoc,\n featSlots: asyncapiSlots(featPlain),\n featPlain,\n featText,\n livingDoc,\n livingSlots: slotsByKey(asyncapiSlots(livingPlain)),\n livingText,\n livingPlain,\n livingAnswers,\n merged,\n };\n}\n"]}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The contract as the feature would leave it: the REAL merge
3
+ * (core/asyncapi/merge/merge.ts, core/openapi/merge/merge.ts) run over the
4
+ * two texts and read back, nothing written. On the event axis one simulation
5
+ * feeds three gate questions — the `declares()` join in ../events/events.ts
6
+ * (a retired or edited-away send must stop answering "the service declares
7
+ * this" BEFORE archive, or a leftover `Publishes:` line passes the gate and
8
+ * breaks the post-archive fleet), the consumer gate's wire half (a channel or
9
+ * operation removal that stops the service SENDING a message the fleet still
10
+ * consumes), and the revival check (./revival/arrivals.ts) — which asks it on both
11
+ * axes: where does each operationId and message name stand once the delta
12
+ * merges, against where it stands now.
13
+ *
14
+ * Calling the merge rather than re-deriving its verdicts is the point: a
15
+ * second spelling of quote/edit/removal here would eventually disagree with
16
+ * the merge about what archive actually writes, and the gate would be
17
+ * promising a contract the archive does not produce. The revival check is
18
+ * the proof: it re-derived the merge case by case — a relocation marker
19
+ * anywhere, a swap by a named operation, a channel restated with a `$ref` —
20
+ * and every review found a shape the derivation missed (a marker at an
21
+ * address the id never lived at, a replacement with no operationId).
22
+ *
23
+ * A living contract that does not exist is the archive's CREATE branch: it
24
+ * writes the delta with its markers stripped, and that is what the merged
25
+ * contract is.
26
+ */
27
+ import { parse } from "yaml";
28
+ import { asyncapiDocOf } from "../../asyncapi/read.js";
29
+ import { AsyncapiMergeError } from "../../asyncapi/merge/error.js";
30
+ import { stripAsyncapiMarkers } from "../../asyncapi/merge/markers.js";
31
+ import { mergeAsyncapiSlots } from "../../asyncapi/merge/merge.js";
32
+ import { openapiFromText } from "../../openapi/doc.js";
33
+ import { OpenapiMergeError } from "../../openapi/merge/error.js";
34
+ import { stripOpenapiRemovalMarkers } from "../../openapi/merge/markers.js";
35
+ import { mergeOpenapiPaths } from "../../openapi/merge/merge.js";
36
+ /**
37
+ * Simulate the archive-time event merge, or stand down. Both documents are
38
+ * known readable here (the pair, ./event-pair.ts, asks only with the delta's
39
+ * text in hand and a living side that answers — readable, or absent — and
40
+ * both of its readers, the walk in ../events/declared.ts and rebase's
41
+ * `serviceRevivals`, refuse an unreadable delta first), so the only
42
+ * refusals left are the merge's own judgement calls — an aliased section, a
43
+ * section spelling one slot key twice. `undefined` on those rather than a
44
+ * throw: the walk falls back to the feature ∪ living union view, which can
45
+ * only be too lenient, the revival check grades no message, and the
46
+ * archive's own plan surfaces the refusal by name — a refusal `--approve`
47
+ * cannot pass, so nothing it would have graded can land.
48
+ */
49
+ export function mergedAsyncapiDoc(input) {
50
+ const { livingText, livingDoc, livingRoot, featureText, service } = input;
51
+ try {
52
+ const text = livingText === undefined ? stripAsyncapiMarkers(featureText, service) : mergeAsyncapiSlots(livingText, featureText, service).text;
53
+ if (text === null)
54
+ return { doc: livingDoc, root: livingRoot };
55
+ const root = parse(text);
56
+ return { doc: asyncapiDocOf(root), root };
57
+ }
58
+ catch (error) {
59
+ if (error instanceof AsyncapiMergeError)
60
+ return undefined;
61
+ throw error;
62
+ }
63
+ }
64
+ /** The OpenAPI twin, through the same function archive's plan calls — `undefined` where that merge refuses. */
65
+ export function mergedOpenapiDoc(input) {
66
+ const { livingText, livingDoc, featureText, service } = input;
67
+ try {
68
+ if (livingText === undefined)
69
+ return openapiFromText(stripOpenapiRemovalMarkers(featureText, service));
70
+ const out = mergeOpenapiPaths(livingText, featureText, service);
71
+ return out.text === null ? livingDoc : openapiFromText(out.text);
72
+ }
73
+ catch (error) {
74
+ if (error instanceof OpenapiMergeError)
75
+ return undefined;
76
+ throw error;
77
+ }
78
+ }
79
+ //# sourceMappingURL=merged.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged.js","sourceRoot":"","sources":["../../../../src/core/coherence/retired/merged.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAmB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAgCjE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;QAC/I,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAY,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO,EAAE,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,+GAA+G;AAC/G,MAAM,UAAU,gBAAgB,CAAC,KAAuC;IACtE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC9D,IAAI,CAAC;QACH,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,eAAe,CAAC,0BAA0B,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QACvG,MAAM,GAAG,GAAG,iBAAiB,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QAChE,OAAO,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB;YAAE,OAAO,SAAS,CAAC;QACzD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["/**\n * The contract as the feature would leave it: the REAL merge\n * (core/asyncapi/merge/merge.ts, core/openapi/merge/merge.ts) run over the\n * two texts and read back, nothing written. On the event axis one simulation\n * feeds three gate questions — the `declares()` join in ../events/events.ts\n * (a retired or edited-away send must stop answering \"the service declares\n * this\" BEFORE archive, or a leftover `Publishes:` line passes the gate and\n * breaks the post-archive fleet), the consumer gate's wire half (a channel or\n * operation removal that stops the service SENDING a message the fleet still\n * consumes), and the revival check (./revival/arrivals.ts) — which asks it on both\n * axes: where does each operationId and message name stand once the delta\n * merges, against where it stands now.\n *\n * Calling the merge rather than re-deriving its verdicts is the point: a\n * second spelling of quote/edit/removal here would eventually disagree with\n * the merge about what archive actually writes, and the gate would be\n * promising a contract the archive does not produce. The revival check is\n * the proof: it re-derived the merge case by case — a relocation marker\n * anywhere, a swap by a named operation, a channel restated with a `$ref` —\n * and every review found a shape the derivation missed (a marker at an\n * address the id never lived at, a replacement with no operationId).\n *\n * A living contract that does not exist is the archive's CREATE branch: it\n * writes the delta with its markers stripped, and that is what the merged\n * contract is.\n */\nimport { parse } from \"yaml\";\nimport { asyncapiDocOf } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport { AsyncapiMergeError } from \"../../asyncapi/merge/error.js\";\nimport { stripAsyncapiMarkers } from \"../../asyncapi/merge/markers.js\";\nimport { mergeAsyncapiSlots } from \"../../asyncapi/merge/merge.js\";\nimport { openapiFromText, type OpenapiDoc } from \"../../openapi/doc.js\";\nimport { OpenapiMergeError } from \"../../openapi/merge/error.js\";\nimport { stripOpenapiRemovalMarkers } from \"../../openapi/merge/markers.js\";\nimport { mergeOpenapiPaths } from \"../../openapi/merge/merge.js\";\n\n/** The two texts the simulation merges, plus the living side's existing parse. */\nexport interface MergeSimulationInput<Doc> {\n /** undefined when no living contract exists — the archive then creates one from the delta. */\n livingText: string | undefined;\n /** The reader's parse of livingText — reused verbatim when the merge writes nothing. */\n livingDoc: Doc;\n featureText: string;\n service: string;\n}\n\n/** The event simulation's input: the pair's texts, plus the living side's tree. */\nexport interface EventMergeInput extends MergeSimulationInput<AsyncapiDoc> {\n /** The reader's parse of livingText — the merged tree, verbatim, when the merge writes nothing. */\n livingRoot: unknown;\n}\n\n/** The contract the event merge would leave: as read, and the tree it was read from. */\nexport interface MergedEvents {\n doc: AsyncapiDoc;\n /**\n * The merged document's parse — where a `$ref` can be followed back to the\n * slot holding it. The read model keeps no pointer, and the move-back\n * remedy (./revival/revived.ts) has to name the channels still pointing at\n * a key it tells the author to retire: told only to retire it, an author\n * left a living channel `$ref`-ing nothing and its send operation stopped\n * sending a message the fleet consumes.\n */\n root: unknown;\n}\n\n/**\n * Simulate the archive-time event merge, or stand down. Both documents are\n * known readable here (the pair, ./event-pair.ts, asks only with the delta's\n * text in hand and a living side that answers — readable, or absent — and\n * both of its readers, the walk in ../events/declared.ts and rebase's\n * `serviceRevivals`, refuse an unreadable delta first), so the only\n * refusals left are the merge's own judgement calls — an aliased section, a\n * section spelling one slot key twice. `undefined` on those rather than a\n * throw: the walk falls back to the feature ∪ living union view, which can\n * only be too lenient, the revival check grades no message, and the\n * archive's own plan surfaces the refusal by name — a refusal `--approve`\n * cannot pass, so nothing it would have graded can land.\n */\nexport function mergedAsyncapiDoc(input: EventMergeInput): MergedEvents | undefined {\n const { livingText, livingDoc, livingRoot, featureText, service } = input;\n try {\n const text = livingText === undefined ? stripAsyncapiMarkers(featureText, service) : mergeAsyncapiSlots(livingText, featureText, service).text;\n if (text === null) return { doc: livingDoc, root: livingRoot };\n const root: unknown = parse(text);\n return { doc: asyncapiDocOf(root), root };\n } catch (error) {\n if (error instanceof AsyncapiMergeError) return undefined;\n throw error;\n }\n}\n\n/** The OpenAPI twin, through the same function archive's plan calls — `undefined` where that merge refuses. */\nexport function mergedOpenapiDoc(input: MergeSimulationInput<OpenapiDoc>): OpenapiDoc | undefined {\n const { livingText, livingDoc, featureText, service } = input;\n try {\n if (livingText === undefined) return openapiFromText(stripOpenapiRemovalMarkers(featureText, service));\n const out = mergeOpenapiPaths(livingText, featureText, service);\n return out.text === null ? livingDoc : openapiFromText(out.text);\n } catch (error) {\n if (error instanceof OpenapiMergeError) return undefined;\n throw error;\n }\n}\n"]}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A relocation — same operationId, removal marker on the old slot, upsert on
3
+ * the new one — retires nothing. Every rule that asks "is this operation going
4
+ * away" must therefore ask the NET set (markers this feature does not
5
+ * redefine), while "did the author write a marker at all" asks the raw one.
6
+ * Conflating them made moving an endpoint fail three checks at once for a
7
+ * change that removes nothing.
8
+ *
9
+ * A marker with no `operationId` is not here at all: the reader lists it apart
10
+ * (`anonymousRemovals`), and the merge deletes a living operation only when the
11
+ * one at the marker's own method and path carries the id the marker names — a
12
+ * marker naming none matches nothing, so it retires nothing either. That match
13
+ * is also why a marker is a REQUEST rather than a record: one that missed its
14
+ * slot deleted nothing, and the retired index (./archived.ts) checks each
15
+ * against the contract its merge ran against before counting it.
16
+ */
17
+ export function operationRetirements(ops) {
18
+ const redefined = new Set(ops.filter((op) => !op.remove).map((op) => op.id));
19
+ const markerIds = new Set(ops.filter((op) => op.remove).map((op) => op.id));
20
+ const net = new Set([...markerIds].filter((id) => !redefined.has(id)));
21
+ return { markerIds, net };
22
+ }
23
+ /**
24
+ * A relocation — same message name, marker on the old key, declaration at a
25
+ * new one — retires nothing. "Is this message going away" asks the NET set;
26
+ * "did the author write a marker" asks the raw one, the OpenAPI distinction
27
+ * above verbatim. Two raw sets, because the two marker shapes carry different
28
+ * debts: a message-slot marker must be justified by a REMOVED requirement,
29
+ * while a channel-slot removal needs exactness only (requirements join on
30
+ * message names, never channel keys — SCHEMA.md) and its living inline
31
+ * interior leaves with it.
32
+ *
33
+ * `living` is the contract the delta was merged INTO (or is about to be): a
34
+ * channel removal's inline interior is written there and nowhere in the delta,
35
+ * which carries only the marker. The feature gate passes the living contract
36
+ * on disk; the retired index passes the archived feature's snapshot pre-image,
37
+ * the living contract as it stood the moment that merge ran.
38
+ */
39
+ export function messageRetirements(featDoc, featSlots, living) {
40
+ const markers = featDoc.messages.filter((m) => m.remove === true && m.slot.startsWith("components.messages."));
41
+ const channelRemovals = featSlots.filter((s) => s.section === "channels" && s.remove).map((s) => s.key);
42
+ const inline = living.messages.filter((m) => channelRemovals.some((ck) => m.slot.startsWith(`channels.${ck}.messages.`)));
43
+ const markerNames = new Set(markers.map((m) => m.name));
44
+ const inlineRetired = new Set(inline.map((m) => m.name));
45
+ const redeclared = new Set(featDoc.messages.filter((m) => m.remove !== true).map((m) => m.name));
46
+ const net = new Set([...markerNames, ...inlineRetired].filter((name) => !redeclared.has(name)));
47
+ return { markerNames, inlineRetired, redeclared, net, vacated: [...markers, ...inline] };
48
+ }
49
+ //# sourceMappingURL=retirement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retirement.js","sourceRoot":"","sources":["../../../../src/core/coherence/retired/retirement.ts"],"names":[],"mappings":"AA6BA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAyB;IAC5D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;AAC5B,CAAC;AAqBD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAoB,EACpB,SAAkC,EAClC,MAAmB;IAEnB,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAC/G,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxG,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1H,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,WAAW,EAAE,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;AAC3F,CAAC","sourcesContent":["/**\n * What ONE contract delta retires — the rule, spelled once for every reader\n * that asks it.\n *\n * Two readers ask it of different features, and they must never disagree. The\n * feature gate asks it of the delta being graded (`../declared.ts` for the\n * OpenAPI axis, `../events/declared.ts` for the event axis): which operations\n * and messages is THIS feature taking out of the living contract. The retired\n * index (`./archived.ts`) asks the same question of every feature already in\n * `features/archive/`: which operations and messages did a shipped feature take\n * out, so a later delta spelling one of them is a revival rather than an\n * addition. A second spelling of the relocation rule in the index is how a\n * moved endpoint would read as retired there while the gate called it moved.\n *\n * Moved here from the two walks verbatim, comments and all; the walks keep the\n * checks that USE the sets, this module keeps the sets.\n */\nimport type { Operation } from \"../../openapi/doc.js\";\nimport type { AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport type { AsyncapiDoc, EventMessage } from \"../../asyncapi/model.js\";\n\n/** The OpenAPI half: every id a removal marker names, and the ones genuinely retired. */\nexport interface OperationRetirements {\n /** Every operationId a removal marker names, relocations included — \"did the author write a marker\". */\n markerIds: Set<string>;\n /** Markers minus relocations — \"is this operation going away\". */\n net: Set<string>;\n}\n\n/**\n * A relocation — same operationId, removal marker on the old slot, upsert on\n * the new one — retires nothing. Every rule that asks \"is this operation going\n * away\" must therefore ask the NET set (markers this feature does not\n * redefine), while \"did the author write a marker at all\" asks the raw one.\n * Conflating them made moving an endpoint fail three checks at once for a\n * change that removes nothing.\n *\n * A marker with no `operationId` is not here at all: the reader lists it apart\n * (`anonymousRemovals`), and the merge deletes a living operation only when the\n * one at the marker's own method and path carries the id the marker names — a\n * marker naming none matches nothing, so it retires nothing either. That match\n * is also why a marker is a REQUEST rather than a record: one that missed its\n * slot deleted nothing, and the retired index (./archived.ts) checks each\n * against the contract its merge ran against before counting it.\n */\nexport function operationRetirements(ops: readonly Operation[]): OperationRetirements {\n const redefined = new Set(ops.filter((op) => !op.remove).map((op) => op.id));\n const markerIds = new Set(ops.filter((op) => op.remove).map((op) => op.id));\n const net = new Set([...markerIds].filter((id) => !redefined.has(id)));\n return { markerIds, net };\n}\n\n/** The event half's four sets — the raw marker shapes, the redeclarations, and what is genuinely going away. */\nexport interface MessageRetirements {\n /** Names a `components.messages` marker retires, relocations included. */\n markerNames: Set<string>;\n /** Names the LIVING contract declares inline under a channel this delta removes. */\n inlineRetired: Set<string>;\n /** Names the delta declares again, anywhere — a marker on one of these is a relocation. */\n redeclared: Set<string>;\n /** `markerNames ∪ inlineRetired`, minus `redeclared`. */\n net: Set<string>;\n /**\n * The declarations behind the first two sets, each where it stood — the\n * `components.messages` markers, and the living inline declarations under\n * a removed channel. What the retired index (./archived.ts) records as the\n * addresses an archived feature vacated, read off this one spelling.\n */\n vacated: EventMessage[];\n}\n\n/**\n * A relocation — same message name, marker on the old key, declaration at a\n * new one — retires nothing. \"Is this message going away\" asks the NET set;\n * \"did the author write a marker\" asks the raw one, the OpenAPI distinction\n * above verbatim. Two raw sets, because the two marker shapes carry different\n * debts: a message-slot marker must be justified by a REMOVED requirement,\n * while a channel-slot removal needs exactness only (requirements join on\n * message names, never channel keys — SCHEMA.md) and its living inline\n * interior leaves with it.\n *\n * `living` is the contract the delta was merged INTO (or is about to be): a\n * channel removal's inline interior is written there and nowhere in the delta,\n * which carries only the marker. The feature gate passes the living contract\n * on disk; the retired index passes the archived feature's snapshot pre-image,\n * the living contract as it stood the moment that merge ran.\n */\nexport function messageRetirements(\n featDoc: AsyncapiDoc,\n featSlots: readonly AsyncapiSlot[],\n living: AsyncapiDoc,\n): MessageRetirements {\n const markers = featDoc.messages.filter((m) => m.remove === true && m.slot.startsWith(\"components.messages.\"));\n const channelRemovals = featSlots.filter((s) => s.section === \"channels\" && s.remove).map((s) => s.key);\n const inline = living.messages.filter((m) => channelRemovals.some((ck) => m.slot.startsWith(`channels.${ck}.messages.`)));\n const markerNames = new Set(markers.map((m) => m.name));\n const inlineRetired = new Set(inline.map((m) => m.name));\n const redeclared = new Set(featDoc.messages.filter((m) => m.remove !== true).map((m) => m.name));\n const net = new Set([...markerNames, ...inlineRetired].filter((name) => !redeclared.has(name)));\n return { markerNames, inlineRetired, redeclared, net, vacated: [...markers, ...inline] };\n}\n"]}