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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -1,18 +1,27 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { loadFile } from "../c4/likec4.js";
3
3
  import { elementService, serviceResolver } from "../c4/resolve/service.js";
4
+ import { infrastructureKind } from "../c4/direction/direction.js";
5
+ import { livingMapOf, readDeltaSystem } from "../c4/resolve/systems.js";
6
+ import { extendingModels } from "../c4/service-model/fleet/extending.js";
4
7
  import { serviceIdProblem } from "../kernel/ids/service.js";
5
8
  import { deltaShapeIssues } from "../delta/delta.js";
6
- import { featurePaths } from "../repo/paths.js";
9
+ import { featurePaths, landscapePath } from "../repo/paths.js";
7
10
  import { featureSpecServices } from "../repo/repo.js";
8
11
  import { enumeratedServiceIds, enumeratedServiceIndex } from "../repo/service-target.js";
9
12
  import { serviceOperationIds } from "../openapi/doc.js";
10
13
  import { declaredByService } from "./declared.js";
14
+ import { retiredLookup } from "./retired/archived.js";
15
+ import { revivedOperations } from "./retired/revival/revived.js";
11
16
  import { coherenceLookups } from "./lookups.js";
17
+ import { consumerIndex } from "../consumers/consumers.js";
18
+ import { consumedOperationIssues } from "./consumed/operations.js";
12
19
  import { eventCoherence } from "./events/events.js";
13
20
  import { authoringIssues } from "./authoring/scaffold.js";
21
+ import { droppedHealthCoverage } from "./covers/health.js";
14
22
  import { glossaryDeltaIssues } from "../glossary/delta.js";
15
23
  import { flowDeltaIssues } from "../usecases/delta/flows.js";
24
+ import { deploymentDeltaIssues, readFeatureDeployments } from "../deployment/delta.js";
16
25
  export async function featureCoherence(request) {
17
26
  const { docsDir, featureDir, featureId, preloadedDelta, context } = request;
18
27
  const scope = { docsDir, featureDir, featureId };
@@ -32,6 +41,47 @@ export async function featureCoherence(request) {
32
41
  // already holds must be caught before the copy, not after it has replaced an
33
42
  // authored hop sequence.
34
43
  issues.push(...(await flowDeltaIssues(docsDir, featureDir)));
44
+ issues.push(...(await deploymentDeltaIssues(docsDir, featureDir)));
45
+ // And the topology's second question, which the collision check above cannot
46
+ // ask: does the document READ against the map this feature's own merge would
47
+ // leave behind. It matters more literally here than one axis over — a feature
48
+ // that stands a service up in a new cluster declares that service in its
49
+ // `delta.likec4` and instances it in the topology, so the element the document
50
+ // names exists only post-merge — and without it the archive copies an
51
+ // unreadable document into `architecture/` for the next reader's
52
+ // `loam validate --all` to fail on, against a file they did not write.
53
+ const topology = await readFeatureDeployments({
54
+ docsDir,
55
+ featureDir,
56
+ featureId,
57
+ // A function rather than the context itself, for the reason the use-case
58
+ // overlay states: one memoised read does not justify an edge from
59
+ // `core/deployment/` to `fleet-context`.
60
+ ...(context === undefined ? {} : { load: (path) => context.loadLikeC4(path) }),
61
+ // The fleet's extending models, so the preview is the map the archive would
62
+ // really leave: a nested addition is routed into the service's own model,
63
+ // never onto the map. Read here for the same reason `load` is passed here —
64
+ // this module holds the context and `core/deployment/` does not — and as a
65
+ // thunk, so a feature with no `deployment/` directory pays nothing for it.
66
+ models: () => extendingModels(docsDir, context),
67
+ });
68
+ if (topology.kind === "unreadable") {
69
+ issues.push({
70
+ severity: "error",
71
+ code: "deployment.doc-invalid",
72
+ subject: featureId,
73
+ // The opening sentence does not say whose file is at fault, on the
74
+ // `usecase.flow-invalid` message's reasoning: the corpus is the feature's
75
+ // `deployment/` PLUS the living `architecture/` documents it extends, so
76
+ // the failing path can be a living one — a landscape that no longer
77
+ // parses, a topology somebody else wrote. Every message carries its own
78
+ // path, so a reader can place it.
79
+ message: `this feature's deployment document(s) could not be read against the map its own merge would leave behind, ` +
80
+ `so the archive would land an architecture/ nothing has checked. The corpus is the feature's deployment/ AND ` +
81
+ `the living architecture/ documents it extends, so the fault may be in either — each message names its file. ` +
82
+ `${topology.errors.length} problem(s): ${topology.errors.join("; ")}`,
83
+ });
84
+ }
35
85
  // --- C4 delta ---
36
86
  let elements = [];
37
87
  let taggedEls = [];
@@ -79,7 +129,8 @@ export async function featureCoherence(request) {
79
129
  // services/ falls back to the feature's own names — every living probe would
80
130
  // have found nothing there anyway.
81
131
  const fleetIds = await enumeratedServiceIds(docsDir, context);
82
- const svcOf = serviceResolver(elements, new Set([...fleetIds, ...svcNames]));
132
+ const known = new Set([...fleetIds, ...svcNames]);
133
+ const svcOf = serviceResolver(elements, known);
83
134
  // Which elements the landscape merge would SPLICE: the tagged ones and
84
135
  // everything nested inside their blocks. Defined here because two checks
85
136
  // share it — the scaffold gate just below, and the binding-grammar check
@@ -92,21 +143,27 @@ export async function featureCoherence(request) {
92
143
  // documents whose merge would publish content nobody authored — the exact
93
144
  // template strings `loam new` wrote (authoring/scaffold.ts owns the list).
94
145
  issues.push(...(await authoringIssues({ featureDir, featureId, splicedEls: elements.filter(ridesWithTag), svcNames, context })));
95
- const { reqOps, removingOps, featureApiOps, unreadableApis, } = await declaredByService(scope, svcNames, issues, context);
96
- const { governedByLivingSpec, edgeConsumers, requirementConsumers, definedElsewhere, deprecatedInLiving, undeprecatedByFeature, } = coherenceLookups(scope, context);
97
- for (const [svc, retiring] of removingOps) {
98
- for (const op of retiring) {
99
- const consumers = [...(await edgeConsumers(svc, op)), ...(await requirementConsumers(svc, op))];
100
- if (consumers.length === 0)
101
- continue;
102
- issues.push({
103
- severity: "error",
104
- code: "openapi.remove-op-consumed",
105
- subject: svc,
106
- message: `${svc}: this feature retires '${op}', but the living fleet still consumes it — ${consumers.join("; ")}. Retire the consumer in the same feature (drop the edge from architecture/landscape.likec4, or the requirement that names it), or archive with --approve to break it deliberately.`,
107
- });
108
- }
146
+ const { reqOps, removingOps, featureApiOps, unreadableApis, revivalCandidates, displacedOps, } = await declaredByService(scope, svcNames, issues, context);
147
+ // A revival: an operation the merged contract would answer that the living
148
+ // contract no longer has because an ARCHIVED feature retired it — or would
149
+ // answer again at an address an archived feature took it out of, beside
150
+ // where it lives now — and the merge would read it as an addition and put
151
+ // it back (./retired/ says why the gate could not see this before). One
152
+ // lazy handle for both contract axes, so the walk of features/archive/ is
153
+ // paid once per call at most, and only when the merged contract answers
154
+ // something at an address the living one does not.
155
+ const retired = retiredLookup(docsDir, context);
156
+ for (const [svc, arrived] of revivalCandidates) {
157
+ if (arrived.length === 0)
158
+ continue;
159
+ issues.push(...revivedOperations(svc, arrived, await retired()));
109
160
  }
161
+ const { governedByLivingSpec, definedElsewhere, deprecatedInLiving, undeprecatedByFeature } = coherenceLookups(scope, context);
162
+ // --- taking an operation out of the contract while the fleet still calls it ---
163
+ // ./consumed/operations.ts owns the gate; the index is built here because
164
+ // the event axis below asks the same one, so the fleet is read once.
165
+ const consumers = consumerIndex({ docsDir, context, afterFeature: featureDir });
166
+ issues.push(...(await consumedOperationIssues({ consumers, removingOps, displacedOps })));
110
167
  // E1: Spec -> API — every operation a requirement governs must exist in that service's OpenAPI.
111
168
  for (const [svc, ops] of reqOps) {
112
169
  // The union below reads the feature's own contract; unreadable means the
@@ -160,7 +217,11 @@ export async function featureCoherence(request) {
160
217
  if (contractUnreadable) {
161
218
  // openapi.invalid (declared.ts) already gates; one breach, one finding.
162
219
  }
163
- else if (pathable !== undefined && removingOps.get(pathable)?.has(r.op) === true) {
220
+ else if (pathable !== undefined &&
221
+ // An operation replaced at its address leaves as surely as a retired
222
+ // one; `available` below still lists it, because serviceOperationIds
223
+ // subtracts markers only, so without this a new edge onto it passed.
224
+ (removingOps.get(pathable)?.has(r.op) === true || displacedOps.get(pathable)?.some((d) => d.id === r.op) === true)) {
164
225
  issues.push({ severity: "error", code: "c4-api.op-removing", message: `${svcOf(r.source)} builds new consumption on '${r.op}', which this feature removes from ${target}` });
165
226
  }
166
227
  else if (!available.includes(r.op)) {
@@ -200,12 +261,28 @@ export async function featureCoherence(request) {
200
261
  issues.push({ severity: "warn", code: "api.op-unconsumed", message: `operation '${op}' is added but no architecture edge consumes it (provider-only or unmodeled)` });
201
262
  }
202
263
  }
203
- // W3: a new service should carry a requirement delta.
264
+ // W3: a new service should carry a requirement delta. "New service" is the
265
+ // fleet census's reading of the map this merge leaves (../c4/resolve/systems.ts):
266
+ // a tagged `#external` producer, a grouping and a container are no service of
267
+ // ours, and asking each for a `specs/` delta put a unit of work nobody owes
268
+ // on the board and a warning the archived fleet never repeats. Nor is a
269
+ // service the fleet already holds new — its own directory, or a box the
270
+ // living map draws — whatever the delta's tag says: re-declaring is not
271
+ // introducing.
272
+ // The living map is parsed only for a candidate no directory answers for.
273
+ const systems = {
274
+ elements,
275
+ known,
276
+ living: new Set(fleetIds),
277
+ livingMap: livingMapOf(landscapePath(docsDir), (path) => (context === undefined ? loadFile(path) : context.loadLikeC4(path))),
278
+ };
204
279
  for (const e of taggedEls) {
205
280
  const svc = elementService(e);
206
- if (e.kind === "softwareSystem" && !svcNames.some((n) => n === svc)) {
207
- issues.push({ severity: "warn", code: "service.no-requirement-delta", message: `new service ${svc} has no requirement delta under specs/` });
208
- }
281
+ if (e.kind !== "softwareSystem" || svcNames.some((n) => n === svc))
282
+ continue;
283
+ if ((await readDeltaSystem(e, systems)) !== "service")
284
+ continue;
285
+ issues.push({ severity: "warn", code: "service.no-requirement-delta", message: `new service ${svc} has no requirement delta under specs/` });
209
286
  }
210
287
  // An explicit `metadata { service '<id>' }` binding, held to the same grammar
211
288
  // every `--service` flag is — on the tagged elements AND everything nested
@@ -245,7 +322,11 @@ export async function featureCoherence(request) {
245
322
  // feature's asyncapi deltas (pins, markers, justification), the tagged
246
323
  // edges' publishes/consumes metadata, and the requirement deltas'
247
324
  // Publishes:/Consumes: lines (./events/events.ts owns the walk).
248
- issues.push(...(await eventCoherence({ scope, svcNames, taggedRels, svcOf, context })));
325
+ const infraKindOf = infrastructureKind(elements);
326
+ issues.push(...(await eventCoherence({ scope, svcNames, taggedRels, svcOf, infraKindOf, context, retired, consumers, known })));
327
+ // The health axis's one merge question: an alert or SLI the living
328
+ // arch.spec.md covers that this delta would leave covered by nothing.
329
+ issues.push(...(await droppedHealthCoverage({ docsDir, featureDir, services: svcNames, ...(context === undefined ? {} : { context }) })));
249
330
  return issues;
250
331
  }
251
332
  //# sourceMappingURL=coherence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"coherence.js","sourceRoot":"","sources":["../../../src/core/coherence/coherence.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { loadFile, type Elem, type LoadedDoc, type Rel } from \"../c4/likec4.js\";\nimport { elementService, serviceResolver } from \"../c4/resolve/service.js\";\nimport { serviceIdProblem } from \"../kernel/ids/service.js\";\nimport { deltaShapeIssues } from \"../delta/delta.js\";\nimport type { Issue } from \"../vocabulary/issue.js\";\nimport { featurePaths } from \"../repo/paths.js\";\nimport { featureSpecServices } from \"../repo/repo.js\";\nimport { enumeratedServiceIds, enumeratedServiceIndex } from \"../repo/service-target.js\";\nimport { serviceOperationIds } from \"../openapi/doc.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { declaredByService, type DeltaScope } from \"./declared.js\";\nimport { coherenceLookups } from \"./lookups.js\";\nimport { eventCoherence } from \"./events/events.js\";\nimport { authoringIssues } from \"./authoring/scaffold.js\";\nimport { glossaryDeltaIssues } from \"../glossary/delta.js\";\nimport { flowDeltaIssues } from \"../usecases/delta/flows.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n\n/**\n * Cross-axis consistency for a feature: do C4 (architecture), requirements (behaviour),\n * and OpenAPI (contract) agree? Errors are hard (would corrupt the living docs on archive);\n * warnings surface softer misalignments.\n *\n * `preloadedDelta` is the feature's already-parsed delta.likec4, when the caller\n * has it — loading a LikeC4 document spins up a fresh Langium workspace, the\n * dominant per-feature cost, and validate/archive have both parsed the same file\n * moments earlier. Standalone calls omit it and nothing changes: the file is\n * loaded here, exactly as before.\n */\n\n/** How a caller names the feature being graded, and what it already has in hand. */\nexport interface CoherenceRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n /** The delta LikeC4 document, when the caller already parsed it. */\n preloadedDelta?: LoadedDoc;\n context?: FleetContext;\n}\n\nexport async function featureCoherence(request: CoherenceRequest): Promise<Issue[]> {\n const { docsDir, featureDir, featureId, preloadedDelta, context } = request;\n const scope: DeltaScope = { docsDir, featureDir, featureId };\n // Delta shape first: a diff that does not apply to the living spec explains\n // everything downstream, and it is the one breach that is silent without a check.\n const issues: Issue[] = await deltaShapeIssues(docsDir, featureDir, featureId, context);\n\n // The vocabulary axis, and it is asked first because it is cheap and\n // independent: one `existsSync` for a feature with no glossary/, and nothing\n // downstream reads its answer. A term this feature introduces that the living\n // glossary already defines is the one thing that can go wrong on a create-only\n // merge, and it must be caught before the copy, not after.\n issues.push(...(await glossaryDeltaIssues(docsDir, featureDir)));\n\n // The use-case axis, and it is asked here for the vocabulary axis's reasons\n // exactly: one walk over a directory that is usually absent, nothing\n // downstream reads its answer, and a create-only merge has one thing that can\n // go wrong. A flow this feature introduces that the living `architecture/`\n // already holds must be caught before the copy, not after it has replaced an\n // authored hop sequence.\n issues.push(...(await flowDeltaIssues(docsDir, featureDir)));\n\n // --- C4 delta ---\n let elements: Elem[] = [];\n let taggedEls: Elem[] = [];\n let taggedRels: Rel[] = [];\n const deltaPath = featurePaths(featureDir).delta;\n if (existsSync(deltaPath)) {\n const res = preloadedDelta ?? (context === undefined ? await loadFile(deltaPath) : await context.loadLikeC4(deltaPath));\n if (res.errors.length > 0) {\n // An unreadable architecture axis can prove nothing — it must never count as coherent.\n issues.push({\n severity: \"error\",\n code: \"delta.invalid\",\n message: `delta.likec4 has ${res.errors.length} parse error(s) — architecture axis unreadable (run \\`loam validate --feature ${featureId}\\`)`,\n });\n } else {\n elements = res.elements;\n taggedEls = res.elements.filter((e) => e.tags.includes(featureId));\n taggedRels = res.relationships.filter((r) => r.tags.includes(featureId));\n // Every rule below filters by the feature tag, so a delta whose author\n // forgot the tags entirely passes every check and archives while merging\n // nothing. Whole-file only: once ANYTHING is tagged, deciding that an\n // untagged edge \"looks intended\" would be guessing.\n if (\n (res.elements.length > 0 || res.relationships.length > 0) &&\n taggedEls.length === 0 &&\n taggedRels.length === 0\n ) {\n issues.push({\n severity: \"error\",\n code: \"delta.nothing-tagged\",\n message: `delta.likec4 declares elements/relationships but none are tagged #${featureId}; loam cannot see untagged changes`,\n });\n }\n }\n }\n // --- per-service specs (requirement operations) + openapi deltas ---\n const svcNames = await featureSpecServices(featureDir, context);\n\n // Every axis below joins on the service id, so endpoints resolve through the\n // element's `metadata { service }` binding — never through what the box is\n // called. The enumerated fleet — `services/` plus this feature's own\n // `specs/` directories, the same union `enumeratedTarget` probes below —\n // rides into the resolver so a delta that models CONTAINERS joins correctly:\n // without it, an edge into `payment.api` resolves to a service called \"api\"\n // that has never existed, and every check below grades the edge against an\n // absent service instead of the one that owns the container. An unenumerable\n // services/ falls back to the feature's own names — every living probe would\n // have found nothing there anyway.\n const fleetIds = await enumeratedServiceIds(docsDir, context);\n const svcOf = serviceResolver(elements, new Set<string>([...fleetIds, ...svcNames]));\n\n // Which elements the landscape merge would SPLICE: the tagged ones and\n // everything nested inside their blocks. Defined here because two checks\n // share it — the scaffold gate just below, and the binding-grammar check\n // near the end of this function — and the merge carries a tagged element's\n // authored block over byte for byte, children included, so a scope narrower\n // than the splice lets a nested child's text ride through unread.\n const taggedIds = taggedEls.map((el) => el.id);\n const ridesWithTag = (el: Elem): boolean =>\n el.tags.includes(featureId) || taggedIds.some((id) => el.id.startsWith(`${id}.`));\n\n // The scaffold gate: placeholder text and an unwritten intent are legal\n // documents whose merge would publish content nobody authored — the exact\n // template strings `loam new` wrote (authoring/scaffold.ts owns the list).\n issues.push(\n ...(await authoringIssues({ featureDir, featureId, splicedEls: elements.filter(ridesWithTag), svcNames, context })),\n );\n const {\n reqOps, removingOps, featureApiOps, unreadableApis,\n } = await declaredByService(scope, svcNames, issues, context);\n const {\n governedByLivingSpec, edgeConsumers, requirementConsumers, definedElsewhere,\n deprecatedInLiving, undeprecatedByFeature,\n } = coherenceLookups(scope, context);\n\n for (const [svc, retiring] of removingOps) {\n for (const op of retiring) {\n const consumers = [...(await edgeConsumers(svc, op)), ...(await requirementConsumers(svc, op))];\n if (consumers.length === 0) continue;\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-op-consumed\",\n subject: svc,\n message: `${svc}: this feature retires '${op}', but the living fleet still consumes it — ${consumers.join(\"; \")}. Retire the consumer in the same feature (drop the edge from architecture/landscape.likec4, or the requirement that names it), or archive with --approve to break it deliberately.`,\n });\n }\n }\n\n // E1: Spec -> API — every operation a requirement governs must exist in that service's OpenAPI.\n for (const [svc, ops] of reqOps) {\n // The union below reads the feature's own contract; unreadable means the\n // op may well be defined in the file nobody could open, and `op-undefined`\n // would point the author at the requirement when the truth is the YAML.\n // `openapi.invalid` (declared.ts) already gates; one breach, one finding.\n if (unreadableApis.has(svc)) continue;\n const available = await serviceOperationIds(docsDir, svc, featureDir, context);\n for (const op of ops) {\n if (available.includes(op)) continue;\n const other = await definedElsewhere(svc, op);\n if (other !== undefined) {\n issues.push({ severity: \"warn\", code: \"spec-api.op-pending\", message: `requirement in ${svc} governs '${op}', defined by in-flight ${other} — archive it first` });\n continue;\n }\n issues.push({ severity: \"error\", code: \"spec-api.op-undefined\", message: `requirement in ${svc} governs '${op}', not defined in ${svc}'s OpenAPI` });\n }\n }\n\n // The declared→enumerated bridge for edge TARGETS. `svcOf` answers with\n // whatever the document wrote — `metadata { service '../../x' }` parses in\n // LikeC4 without one error — and every living lookup below joins its answer\n // into `services/<target>/` or `specs/<target>/`. So a target resolves\n // through the enumerations first (the fleet's `services/` plus this\n // feature's own `specs/`, where a service it introduces lives), and a name\n // neither knows skips the lookups — no ops, not governed, not deprecated —\n // grading exactly as an absent service, spelled with the declared name.\n const enumeratedTarget = enumeratedServiceIndex(docsDir, svcNames, context);\n\n // E2 / W1 / W4: C4 edges vs API + requirements.\n for (const r of taggedRels) {\n if (r.op === undefined) {\n if ((r.title ?? \"\").toLowerCase().startsWith(\"call\")) {\n issues.push({ severity: \"warn\", code: \"c4.op-link-missing\", message: `edge ${svcOf(r.source)} → ${svcOf(r.target)} (\"${r.title}\") has no operation link (metadata { op })` });\n }\n continue;\n }\n const target = svcOf(r.target);\n const pathable = await enumeratedTarget(target);\n // Same suspension as E1, but only for the verdicts that READ a contract:\n // does the op exist, is it being removed, is it deprecated (whose\n // un-deprecation escape reads the feature contract too). Each of those\n // would be an opinion about a file nobody could open. The governance\n // question below is deliberately NOT suspended — it joins the edge against\n // requirement documents alone, so the broken YAML could never change its\n // answer, and silencing it deferred a real warning for no reason.\n const contractUnreadable = pathable !== undefined && unreadableApis.has(pathable);\n const available =\n pathable === undefined || contractUnreadable\n ? []\n : await serviceOperationIds(docsDir, pathable, featureDir, context);\n if (contractUnreadable) {\n // openapi.invalid (declared.ts) already gates; one breach, one finding.\n } else if (pathable !== undefined && removingOps.get(pathable)?.has(r.op) === true) {\n issues.push({ severity: \"error\", code: \"c4-api.op-removing\", message: `${svcOf(r.source)} builds new consumption on '${r.op}', which this feature removes from ${target}` });\n } else if (!available.includes(r.op)) {\n const other = await definedElsewhere(target, r.op);\n if (other !== undefined) {\n issues.push({ severity: \"warn\", code: \"c4-api.op-pending\", message: `${svcOf(r.source)} calls '${r.op}' on ${target}, defined by in-flight ${other} — archive it first` });\n } else {\n issues.push({ severity: \"error\", code: \"c4-api.op-undefined\", message: `${svcOf(r.source)} calls '${r.op}' on ${target}, but ${target}'s OpenAPI does not define it (contract broken)` });\n }\n }\n // Governed by THIS target's requirements, not by anybody's. An operationId\n // is unique only inside one contract, so the fleet-wide set this used to\n // ask meant a `getStatus` governed in service A silenced the question for\n // service B's entirely unrelated `getStatus` — the warning went quiet for\n // the edge that needed it, and only in repos where some other service\n // happened to reuse the name. The living half on the same line has always\n // joined per service (`governedByLivingSpec(target, …)`); this is the delta\n // half asking it the same way.\n if (pathable === undefined || (!(reqOps.get(pathable) ?? []).includes(r.op) && !(await governedByLivingSpec(pathable, r.op)))) {\n issues.push({ severity: \"warn\", code: \"c4.op-ungoverned\", message: `'${r.op}' is called by ${svcOf(r.source)} but no requirement governs it` });\n }\n // Lifecycle: this NEW tagged edge builds consumption on an operation the\n // living provider contract already marks deprecated. Advisory, never an\n // archive gate — the edge is legal and the contract holds — but new\n // consumption of a dying op deserves an eye before it ships. Quiet when\n // the feature's own openapi delta drops the flag: that state is the fix\n // in progress, not new consumption of a dying op.\n if (!contractUnreadable && pathable !== undefined && (await deprecatedInLiving(pathable, r.op)) && !(await undeprecatedByFeature(pathable, r.op))) {\n issues.push({ severity: \"warn\", code: \"c4-api.op-deprecated\", message: `${svcOf(r.source)} builds new consumption on '${r.op}', which ${target}'s living OpenAPI marks deprecated — prefer the replacement operation` });\n }\n }\n\n // W2: API -> C4 — every operation the feature adds should be consumed by some edge.\n const consumed = new Set(taggedRels.map((r) => r.op).filter((o): o is string => o !== undefined));\n for (const op of featureApiOps) {\n if (!consumed.has(op)) {\n issues.push({ severity: \"warn\", code: \"api.op-unconsumed\", message: `operation '${op}' is added but no architecture edge consumes it (provider-only or unmodeled)` });\n }\n }\n\n // W3: a new service should carry a requirement delta.\n for (const e of taggedEls) {\n const svc: string = elementService(e);\n if (e.kind === \"softwareSystem\" && !svcNames.some((n) => n === svc)) {\n issues.push({ severity: \"warn\", code: \"service.no-requirement-delta\", message: `new service ${svc} has no requirement delta under specs/` });\n }\n }\n\n // An explicit `metadata { service '<id>' }` binding, held to the same grammar\n // every `--service` flag is — on the tagged elements AND everything nested\n // inside their blocks. The scope is the splice's, not the tag's: the\n // landscape merge carries a tagged element's authored block over byte for\n // byte, children included (landscape-merge.ts's rides() exists exactly so a\n // child travels inside its parent's text), so an untagged container's\n // binding reaches the living map as surely as its tagged parent's — and\n // checking only the tagged elements themselves let it ride through unread.\n // The binding is the one name the archive TRUSTS: the merge splices it into\n // the living map verbatim, and the new-service scan probes\n // `services/<binding>/` with it — where `metadata { service '../outside-svc' }`,\n // which LikeC4 parses without a murmur, collapses the probe right out of\n // services/, and the spliced map fails the very next `validate --all`\n // (`landscape.binding-unknown`). Explicit bindings only, on purpose: a prose\n // title with no binding is legal C4, and a title becomes a path only through\n // `specs/<svc>/`, whose own grammar check (`delta.service-id-invalid`)\n // guards that route. `ridesWithTag` itself is defined up beside the scaffold\n // gate, which shares the splice's scope for the same reason.\n for (const e of elements.filter(ridesWithTag)) {\n if (e.service === undefined) continue;\n const problem = serviceIdProblem(e.service, \"metadata { service } binding\");\n if (problem === null) continue;\n issues.push({\n severity: \"error\",\n code: \"c4.service-binding-invalid\",\n subject: e.service,\n message:\n `'${e.title}' (${e.id}) — ${problem} ` +\n `\\`loam archive\\` would splice '${e.service}' into the living landscape.likec4 exactly as written, ` +\n `and probe services/${e.service}/ with it. Bind the element to the services/<id>/ directory it means.`,\n });\n }\n\n // The event axis — the same three-way agreement the operation checks above\n // grade, joined on the message name instead of the operationId: the\n // feature's asyncapi deltas (pins, markers, justification), the tagged\n // edges' publishes/consumes metadata, and the requirement deltas'\n // Publishes:/Consumes: lines (./events/events.ts owns the walk).\n issues.push(...(await eventCoherence({ scope, svcNames, taggedRels, svcOf, context })));\n\n return issues;\n}\n"]}
1
+ {"version":3,"file":"coherence.js","sourceRoot":"","sources":["../../../src/core/coherence/coherence.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { loadFile, type Elem, type LoadedDoc, type Rel } from \"../c4/likec4.js\";\nimport { elementService, serviceResolver } from \"../c4/resolve/service.js\";\nimport { infrastructureKind } from \"../c4/direction/direction.js\";\nimport { livingMapOf, readDeltaSystem } from \"../c4/resolve/systems.js\";\nimport { extendingModels } from \"../c4/service-model/fleet/extending.js\";\nimport { serviceIdProblem } from \"../kernel/ids/service.js\";\nimport { deltaShapeIssues } from \"../delta/delta.js\";\nimport type { Issue } from \"../vocabulary/issue.js\";\nimport { featurePaths, landscapePath } from \"../repo/paths.js\";\nimport { featureSpecServices } from \"../repo/repo.js\";\nimport { enumeratedServiceIds, enumeratedServiceIndex } from \"../repo/service-target.js\";\nimport { serviceOperationIds } from \"../openapi/doc.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { declaredByService, type DeltaScope } from \"./declared.js\";\nimport { retiredLookup } from \"./retired/archived.js\";\nimport { revivedOperations } from \"./retired/revival/revived.js\";\nimport { coherenceLookups } from \"./lookups.js\";\nimport { consumerIndex } from \"../consumers/consumers.js\";\nimport { consumedOperationIssues } from \"./consumed/operations.js\";\nimport { eventCoherence } from \"./events/events.js\";\nimport { authoringIssues } from \"./authoring/scaffold.js\";\nimport { droppedHealthCoverage } from \"./covers/health.js\";\nimport { glossaryDeltaIssues } from \"../glossary/delta.js\";\nimport { flowDeltaIssues } from \"../usecases/delta/flows.js\";\nimport { deploymentDeltaIssues, readFeatureDeployments } from \"../deployment/delta.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * Cross-axis consistency for a feature: do C4 (architecture), requirements (behaviour),\n * and OpenAPI (contract) agree? Errors are hard (would corrupt the living docs on archive);\n * warnings surface softer misalignments.\n *\n * `preloadedDelta` is the feature's already-parsed delta.likec4, when the caller\n * has it — loading a LikeC4 document spins up a fresh Langium workspace, the\n * dominant per-feature cost, and validate/archive have both parsed the same file\n * moments earlier. Standalone calls omit it and nothing changes: the file is\n * loaded here, exactly as before.\n */\n\n/** How a caller names the feature being graded, and what it already has in hand. */\nexport interface CoherenceRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n /** The delta LikeC4 document, when the caller already parsed it. */\n preloadedDelta?: LoadedDoc;\n context?: FleetContext;\n}\n\nexport async function featureCoherence(request: CoherenceRequest): Promise<Issue[]> {\n const { docsDir, featureDir, featureId, preloadedDelta, context } = request;\n const scope: DeltaScope = { docsDir, featureDir, featureId };\n // Delta shape first: a diff that does not apply to the living spec explains\n // everything downstream, and it is the one breach that is silent without a check.\n const issues: Issue[] = await deltaShapeIssues(docsDir, featureDir, featureId, context);\n\n // The vocabulary axis, and it is asked first because it is cheap and\n // independent: one `existsSync` for a feature with no glossary/, and nothing\n // downstream reads its answer. A term this feature introduces that the living\n // glossary already defines is the one thing that can go wrong on a create-only\n // merge, and it must be caught before the copy, not after.\n issues.push(...(await glossaryDeltaIssues(docsDir, featureDir)));\n\n // The use-case axis, and it is asked here for the vocabulary axis's reasons\n // exactly: one walk over a directory that is usually absent, nothing\n // downstream reads its answer, and a create-only merge has one thing that can\n // go wrong. A flow this feature introduces that the living `architecture/`\n // already holds must be caught before the copy, not after it has replaced an\n // authored hop sequence.\n issues.push(...(await flowDeltaIssues(docsDir, featureDir)));\n issues.push(...(await deploymentDeltaIssues(docsDir, featureDir)));\n\n // And the topology's second question, which the collision check above cannot\n // ask: does the document READ against the map this feature's own merge would\n // leave behind. It matters more literally here than one axis over — a feature\n // that stands a service up in a new cluster declares that service in its\n // `delta.likec4` and instances it in the topology, so the element the document\n // names exists only post-merge — and without it the archive copies an\n // unreadable document into `architecture/` for the next reader's\n // `loam validate --all` to fail on, against a file they did not write.\n const topology = await readFeatureDeployments({\n docsDir,\n featureDir,\n featureId,\n // A function rather than the context itself, for the reason the use-case\n // overlay states: one memoised read does not justify an edge from\n // `core/deployment/` to `fleet-context`.\n ...(context === undefined ? {} : { load: (path: string) => context.loadLikeC4(path) }),\n // The fleet's extending models, so the preview is the map the archive would\n // really leave: a nested addition is routed into the service's own model,\n // never onto the map. Read here for the same reason `load` is passed here —\n // this module holds the context and `core/deployment/` does not — and as a\n // thunk, so a feature with no `deployment/` directory pays nothing for it.\n models: () => extendingModels(docsDir, context),\n });\n if (topology.kind === \"unreadable\") {\n issues.push({\n severity: \"error\",\n code: \"deployment.doc-invalid\",\n subject: featureId,\n // The opening sentence does not say whose file is at fault, on the\n // `usecase.flow-invalid` message's reasoning: the corpus is the feature's\n // `deployment/` PLUS the living `architecture/` documents it extends, so\n // the failing path can be a living one — a landscape that no longer\n // parses, a topology somebody else wrote. Every message carries its own\n // path, so a reader can place it.\n message:\n `this feature's deployment document(s) could not be read against the map its own merge would leave behind, ` +\n `so the archive would land an architecture/ nothing has checked. The corpus is the feature's deployment/ AND ` +\n `the living architecture/ documents it extends, so the fault may be in either — each message names its file. ` +\n `${topology.errors.length} problem(s): ${topology.errors.join(\"; \")}`,\n });\n }\n\n // --- C4 delta ---\n let elements: Elem[] = [];\n let taggedEls: Elem[] = [];\n let taggedRels: Rel[] = [];\n const deltaPath = featurePaths(featureDir).delta;\n if (existsSync(deltaPath)) {\n const res = preloadedDelta ?? (context === undefined ? await loadFile(deltaPath) : await context.loadLikeC4(deltaPath));\n if (res.errors.length > 0) {\n // An unreadable architecture axis can prove nothing — it must never count as coherent.\n issues.push({\n severity: \"error\",\n code: \"delta.invalid\",\n message: `delta.likec4 has ${res.errors.length} parse error(s) — architecture axis unreadable (run \\`loam validate --feature ${featureId}\\`)`,\n });\n } else {\n elements = res.elements;\n taggedEls = res.elements.filter((e) => e.tags.includes(featureId));\n taggedRels = res.relationships.filter((r) => r.tags.includes(featureId));\n // Every rule below filters by the feature tag, so a delta whose author\n // forgot the tags entirely passes every check and archives while merging\n // nothing. Whole-file only: once ANYTHING is tagged, deciding that an\n // untagged edge \"looks intended\" would be guessing.\n if (\n (res.elements.length > 0 || res.relationships.length > 0) &&\n taggedEls.length === 0 &&\n taggedRels.length === 0\n ) {\n issues.push({\n severity: \"error\",\n code: \"delta.nothing-tagged\",\n message: `delta.likec4 declares elements/relationships but none are tagged #${featureId}; loam cannot see untagged changes`,\n });\n }\n }\n }\n // --- per-service specs (requirement operations) + openapi deltas ---\n const svcNames = await featureSpecServices(featureDir, context);\n\n // Every axis below joins on the service id, so endpoints resolve through the\n // element's `metadata { service }` binding — never through what the box is\n // called. The enumerated fleet — `services/` plus this feature's own\n // `specs/` directories, the same union `enumeratedTarget` probes below —\n // rides into the resolver so a delta that models CONTAINERS joins correctly:\n // without it, an edge into `payment.api` resolves to a service called \"api\"\n // that has never existed, and every check below grades the edge against an\n // absent service instead of the one that owns the container. An unenumerable\n // services/ falls back to the feature's own names — every living probe would\n // have found nothing there anyway.\n const fleetIds = await enumeratedServiceIds(docsDir, context);\n const known = new Set([...fleetIds, ...svcNames]);\n const svcOf = serviceResolver(elements, known);\n\n // Which elements the landscape merge would SPLICE: the tagged ones and\n // everything nested inside their blocks. Defined here because two checks\n // share it — the scaffold gate just below, and the binding-grammar check\n // near the end of this function — and the merge carries a tagged element's\n // authored block over byte for byte, children included, so a scope narrower\n // than the splice lets a nested child's text ride through unread.\n const taggedIds = taggedEls.map((el) => el.id);\n const ridesWithTag = (el: Elem): boolean =>\n el.tags.includes(featureId) || taggedIds.some((id) => el.id.startsWith(`${id}.`));\n\n // The scaffold gate: placeholder text and an unwritten intent are legal\n // documents whose merge would publish content nobody authored — the exact\n // template strings `loam new` wrote (authoring/scaffold.ts owns the list).\n issues.push(\n ...(await authoringIssues({ featureDir, featureId, splicedEls: elements.filter(ridesWithTag), svcNames, context })),\n );\n const {\n reqOps, removingOps, featureApiOps, unreadableApis, revivalCandidates, displacedOps,\n } = await declaredByService(scope, svcNames, issues, context);\n\n // A revival: an operation the merged contract would answer that the living\n // contract no longer has because an ARCHIVED feature retired it — or would\n // answer again at an address an archived feature took it out of, beside\n // where it lives now — and the merge would read it as an addition and put\n // it back (./retired/ says why the gate could not see this before). One\n // lazy handle for both contract axes, so the walk of features/archive/ is\n // paid once per call at most, and only when the merged contract answers\n // something at an address the living one does not.\n const retired = retiredLookup(docsDir, context);\n for (const [svc, arrived] of revivalCandidates) {\n if (arrived.length === 0) continue;\n issues.push(...revivedOperations(svc, arrived, await retired()));\n }\n const { governedByLivingSpec, definedElsewhere, deprecatedInLiving, undeprecatedByFeature } = coherenceLookups(scope, context);\n\n // --- taking an operation out of the contract while the fleet still calls it ---\n // ./consumed/operations.ts owns the gate; the index is built here because\n // the event axis below asks the same one, so the fleet is read once.\n const consumers = consumerIndex({ docsDir, context, afterFeature: featureDir });\n issues.push(...(await consumedOperationIssues({ consumers, removingOps, displacedOps })));\n\n // E1: Spec -> API — every operation a requirement governs must exist in that service's OpenAPI.\n for (const [svc, ops] of reqOps) {\n // The union below reads the feature's own contract; unreadable means the\n // op may well be defined in the file nobody could open, and `op-undefined`\n // would point the author at the requirement when the truth is the YAML.\n // `openapi.invalid` (declared.ts) already gates; one breach, one finding.\n if (unreadableApis.has(svc)) continue;\n const available = await serviceOperationIds(docsDir, svc, featureDir, context);\n for (const op of ops) {\n if (available.includes(op)) continue;\n const other = await definedElsewhere(svc, op);\n if (other !== undefined) {\n issues.push({ severity: \"warn\", code: \"spec-api.op-pending\", message: `requirement in ${svc} governs '${op}', defined by in-flight ${other} — archive it first` });\n continue;\n }\n issues.push({ severity: \"error\", code: \"spec-api.op-undefined\", message: `requirement in ${svc} governs '${op}', not defined in ${svc}'s OpenAPI` });\n }\n }\n\n // The declared→enumerated bridge for edge TARGETS. `svcOf` answers with\n // whatever the document wrote — `metadata { service '../../x' }` parses in\n // LikeC4 without one error — and every living lookup below joins its answer\n // into `services/<target>/` or `specs/<target>/`. So a target resolves\n // through the enumerations first (the fleet's `services/` plus this\n // feature's own `specs/`, where a service it introduces lives), and a name\n // neither knows skips the lookups — no ops, not governed, not deprecated —\n // grading exactly as an absent service, spelled with the declared name.\n const enumeratedTarget = enumeratedServiceIndex(docsDir, svcNames, context);\n\n // E2 / W1 / W4: C4 edges vs API + requirements.\n for (const r of taggedRels) {\n if (r.op === undefined) {\n if ((r.title ?? \"\").toLowerCase().startsWith(\"call\")) {\n issues.push({ severity: \"warn\", code: \"c4.op-link-missing\", message: `edge ${svcOf(r.source)} → ${svcOf(r.target)} (\"${r.title}\") has no operation link (metadata { op })` });\n }\n continue;\n }\n const target = svcOf(r.target);\n const pathable = await enumeratedTarget(target);\n // Same suspension as E1, but only for the verdicts that READ a contract:\n // does the op exist, is it being removed, is it deprecated (whose\n // un-deprecation escape reads the feature contract too). Each of those\n // would be an opinion about a file nobody could open. The governance\n // question below is deliberately NOT suspended — it joins the edge against\n // requirement documents alone, so the broken YAML could never change its\n // answer, and silencing it deferred a real warning for no reason.\n const contractUnreadable = pathable !== undefined && unreadableApis.has(pathable);\n const available =\n pathable === undefined || contractUnreadable\n ? []\n : await serviceOperationIds(docsDir, pathable, featureDir, context);\n if (contractUnreadable) {\n // openapi.invalid (declared.ts) already gates; one breach, one finding.\n } else if (\n pathable !== undefined &&\n // An operation replaced at its address leaves as surely as a retired\n // one; `available` below still lists it, because serviceOperationIds\n // subtracts markers only, so without this a new edge onto it passed.\n (removingOps.get(pathable)?.has(r.op) === true || displacedOps.get(pathable)?.some((d) => d.id === r.op) === true)\n ) {\n issues.push({ severity: \"error\", code: \"c4-api.op-removing\", message: `${svcOf(r.source)} builds new consumption on '${r.op}', which this feature removes from ${target}` });\n } else if (!available.includes(r.op)) {\n const other = await definedElsewhere(target, r.op);\n if (other !== undefined) {\n issues.push({ severity: \"warn\", code: \"c4-api.op-pending\", message: `${svcOf(r.source)} calls '${r.op}' on ${target}, defined by in-flight ${other} — archive it first` });\n } else {\n issues.push({ severity: \"error\", code: \"c4-api.op-undefined\", message: `${svcOf(r.source)} calls '${r.op}' on ${target}, but ${target}'s OpenAPI does not define it (contract broken)` });\n }\n }\n // Governed by THIS target's requirements, not by anybody's. An operationId\n // is unique only inside one contract, so the fleet-wide set this used to\n // ask meant a `getStatus` governed in service A silenced the question for\n // service B's entirely unrelated `getStatus` — the warning went quiet for\n // the edge that needed it, and only in repos where some other service\n // happened to reuse the name. The living half on the same line has always\n // joined per service (`governedByLivingSpec(target, …)`); this is the delta\n // half asking it the same way.\n if (pathable === undefined || (!(reqOps.get(pathable) ?? []).includes(r.op) && !(await governedByLivingSpec(pathable, r.op)))) {\n issues.push({ severity: \"warn\", code: \"c4.op-ungoverned\", message: `'${r.op}' is called by ${svcOf(r.source)} but no requirement governs it` });\n }\n // Lifecycle: this NEW tagged edge builds consumption on an operation the\n // living provider contract already marks deprecated. Advisory, never an\n // archive gate — the edge is legal and the contract holds — but new\n // consumption of a dying op deserves an eye before it ships. Quiet when\n // the feature's own openapi delta drops the flag: that state is the fix\n // in progress, not new consumption of a dying op.\n if (!contractUnreadable && pathable !== undefined && (await deprecatedInLiving(pathable, r.op)) && !(await undeprecatedByFeature(pathable, r.op))) {\n issues.push({ severity: \"warn\", code: \"c4-api.op-deprecated\", message: `${svcOf(r.source)} builds new consumption on '${r.op}', which ${target}'s living OpenAPI marks deprecated — prefer the replacement operation` });\n }\n }\n\n // W2: API -> C4 — every operation the feature adds should be consumed by some edge.\n const consumed = new Set(taggedRels.map((r) => r.op).filter((o): o is string => o !== undefined));\n for (const op of featureApiOps) {\n if (!consumed.has(op)) {\n issues.push({ severity: \"warn\", code: \"api.op-unconsumed\", message: `operation '${op}' is added but no architecture edge consumes it (provider-only or unmodeled)` });\n }\n }\n\n // W3: a new service should carry a requirement delta. \"New service\" is the\n // fleet census's reading of the map this merge leaves (../c4/resolve/systems.ts):\n // a tagged `#external` producer, a grouping and a container are no service of\n // ours, and asking each for a `specs/` delta put a unit of work nobody owes\n // on the board and a warning the archived fleet never repeats. Nor is a\n // service the fleet already holds new — its own directory, or a box the\n // living map draws — whatever the delta's tag says: re-declaring is not\n // introducing.\n // The living map is parsed only for a candidate no directory answers for.\n const systems = {\n elements,\n known,\n living: new Set<string>(fleetIds),\n livingMap: livingMapOf(landscapePath(docsDir), (path) => (context === undefined ? loadFile(path) : context.loadLikeC4(path))),\n };\n for (const e of taggedEls) {\n const svc: string = elementService(e);\n if (e.kind !== \"softwareSystem\" || svcNames.some((n) => n === svc)) continue;\n if ((await readDeltaSystem(e, systems)) !== \"service\") continue;\n issues.push({ severity: \"warn\", code: \"service.no-requirement-delta\", message: `new service ${svc} has no requirement delta under specs/` });\n }\n\n // An explicit `metadata { service '<id>' }` binding, held to the same grammar\n // every `--service` flag is — on the tagged elements AND everything nested\n // inside their blocks. The scope is the splice's, not the tag's: the\n // landscape merge carries a tagged element's authored block over byte for\n // byte, children included (landscape-merge.ts's rides() exists exactly so a\n // child travels inside its parent's text), so an untagged container's\n // binding reaches the living map as surely as its tagged parent's — and\n // checking only the tagged elements themselves let it ride through unread.\n // The binding is the one name the archive TRUSTS: the merge splices it into\n // the living map verbatim, and the new-service scan probes\n // `services/<binding>/` with it — where `metadata { service '../outside-svc' }`,\n // which LikeC4 parses without a murmur, collapses the probe right out of\n // services/, and the spliced map fails the very next `validate --all`\n // (`landscape.binding-unknown`). Explicit bindings only, on purpose: a prose\n // title with no binding is legal C4, and a title becomes a path only through\n // `specs/<svc>/`, whose own grammar check (`delta.service-id-invalid`)\n // guards that route. `ridesWithTag` itself is defined up beside the scaffold\n // gate, which shares the splice's scope for the same reason.\n for (const e of elements.filter(ridesWithTag)) {\n if (e.service === undefined) continue;\n const problem = serviceIdProblem(e.service, \"metadata { service } binding\");\n if (problem === null) continue;\n issues.push({\n severity: \"error\",\n code: \"c4.service-binding-invalid\",\n subject: e.service,\n message:\n `'${e.title}' (${e.id}) — ${problem} ` +\n `\\`loam archive\\` would splice '${e.service}' into the living landscape.likec4 exactly as written, ` +\n `and probe services/${e.service}/ with it. Bind the element to the services/<id>/ directory it means.`,\n });\n }\n\n // The event axis — the same three-way agreement the operation checks above\n // grade, joined on the message name instead of the operationId: the\n // feature's asyncapi deltas (pins, markers, justification), the tagged\n // edges' publishes/consumes metadata, and the requirement deltas'\n // Publishes:/Consumes: lines (./events/events.ts owns the walk).\n const infraKindOf = infrastructureKind(elements);\n issues.push(...(await eventCoherence({ scope, svcNames, taggedRels, svcOf, infraKindOf, context, retired, consumers, known })));\n\n // The health axis's one merge question: an alert or SLI the living\n // arch.spec.md covers that this delta would leave covered by nothing.\n issues.push(...(await droppedHealthCoverage({ docsDir, featureDir, services: svcNames, ...(context === undefined ? {} : { context }) })));\n\n return issues;\n}\n"]}
@@ -0,0 +1,70 @@
1
+ import { nothingStill, refusalDetails, refusalMessage, stillNamed } from "./refusal.js";
2
+ export async function consumedOperationIssues(request) {
3
+ const { consumers, removingOps, displacedOps } = request;
4
+ const issues = [];
5
+ // --- taking an operation out of the contract while the fleet still calls it ---
6
+ // Exactness and justification never ask what matters to the other
7
+ // ninety-nine repos: is anybody still calling it? The merge deletes the
8
+ // operation while the edge, the requirement and the use-case hop naming it
9
+ // stay, so the next `validate --all` breaks a repository whose author was
10
+ // never in this feature. Asked of the ONE consumer index, read after this
11
+ // feature's merge (core/consumers/ says what it reads and what a feature can
12
+ // retire). `own` counts too: for a marker the REMOVED requirement leaves with
13
+ // the merge, so what remains is a second one, or an internal edge. One index
14
+ // for both shapes and the event axis, so the fleet is read once. A consumer
15
+ // document the index could not read or place refuses too (./refusal.ts):
16
+ // "nobody could look" never let a removal through before the one index.
17
+ const still = async (svc, op) => stillNamed(await consumers.opConsumers(svc, op));
18
+ // The landscape merge only ADDS edges, so an edge is dropped or redrawn in the living map itself.
19
+ const route = `drop or redraw its edge in the architecture/ project, MODIFY or REMOVE the requirement that names it in this feature's specs/<service>/ delta`;
20
+ for (const [svc, retiring] of removingOps) {
21
+ for (const op of retiring) {
22
+ const found = await still(svc, op);
23
+ if (nothingStill(found))
24
+ continue;
25
+ issues.push({
26
+ severity: "error",
27
+ code: "openapi.remove-op-consumed",
28
+ subject: svc,
29
+ details: refusalDetails(found),
30
+ message: refusalMessage(found, {
31
+ subject: svc,
32
+ lost: `this feature retires '${op}'`,
33
+ verb: "consume",
34
+ named: (list) => `${svc}: this feature retires '${op}', but the fleet would still consume it after this merge — ${list}. Retire each consumer in the same change (${route}), or archive with --approve to break it deliberately.`,
35
+ }),
36
+ });
37
+ }
38
+ }
39
+ // The same loss with no marker to read: another operation defined where a
40
+ // consumed one lives replaces it there, and once the contract the merge
41
+ // would write defines it nowhere it has left the contract exactly as a
42
+ // retirement does. One code, because the breach and its override are the
43
+ // removal's — only `api.op-unconsumed` (advisory) said anything before, and
44
+ // the archive passed without `--approve`. With no marker there is no REMOVED
45
+ // requirement either, so the provider's own requirement naming the old id
46
+ // stays unless this feature restates it — `own` is what catches it.
47
+ for (const [svc, displaced] of displacedOps) {
48
+ for (const { id: op, at } of displaced) {
49
+ const found = await still(svc, op);
50
+ if (nothingStill(found))
51
+ continue;
52
+ const by = at.map(({ address, by: id }) => `${id === undefined ? "an operation with no operationId" : `'${id}'`} at ${address}`).join(" and ");
53
+ const lost = `this feature's openapi.yaml defines ${by}, where '${op}' lives now — the merge replaces it there and would leave '${op}' defined nowhere`;
54
+ issues.push({
55
+ severity: "error",
56
+ code: "openapi.remove-op-consumed",
57
+ subject: svc,
58
+ details: refusalDetails(found),
59
+ message: refusalMessage(found, {
60
+ subject: svc,
61
+ lost,
62
+ verb: "consume",
63
+ named: (list) => `${svc}: ${lost}, but it is still consumed — ${list}. Define '${op}' at another address in this delta if it is to stay. To take it out on purpose, update every consumer in the same change — ${route} (${svc}'s own restated to name the new operation, for a rename; a REMOVED one owes no x-loam-remove marker, since the overwrite already takes '${op}' out) — or archive with --approve to break it deliberately.`,
64
+ }),
65
+ });
66
+ }
67
+ }
68
+ return issues;
69
+ }
70
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../../src/core/coherence/consumed/operations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The contract axis's removal gate, `openapi.remove-op-consumed`: does the\n * fleet still consume an operation this feature takes out of the contract —\n * by a removal marker, or by defining another operation where it lives?\n *\n * Moved out of `../coherence.ts`, which ran at its 400-line limit, along the\n * seam the gate already had there: everything below asks the consumer index\n * (core/consumers/) one question per operation the merge loses and phrases\n * the refusal, and nothing else in featureCoherence reads its answer. The\n * index itself is built there and handed in, because the event axis's twin\n * (`asyncapi.remove-message-consumed`, ../events/events.ts) asks the same one.\n */\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport type { ConsumerIndex } from \"../../consumers/consumers.js\";\nimport type { Declared } from \"../declared.js\";\nimport { nothingStill, refusalDetails, refusalMessage, stillNamed, type StillNamed } from \"./refusal.js\";\n\n/** What the gate is asked over: the shared index, and the two ways this feature's merge loses an operation. */\nexport interface ConsumedOperationsRequest {\n /** The consumer index, read after this feature's merge — shared with the event axis so the fleet is read once. */\n consumers: ConsumerIndex;\n removingOps: Declared[\"removingOps\"];\n displacedOps: Declared[\"displacedOps\"];\n}\n\nexport async function consumedOperationIssues(request: ConsumedOperationsRequest): Promise<Issue[]> {\n const { consumers, removingOps, displacedOps } = request;\n const issues: Issue[] = [];\n // --- taking an operation out of the contract while the fleet still calls it ---\n // Exactness and justification never ask what matters to the other\n // ninety-nine repos: is anybody still calling it? The merge deletes the\n // operation while the edge, the requirement and the use-case hop naming it\n // stay, so the next `validate --all` breaks a repository whose author was\n // never in this feature. Asked of the ONE consumer index, read after this\n // feature's merge (core/consumers/ says what it reads and what a feature can\n // retire). `own` counts too: for a marker the REMOVED requirement leaves with\n // the merge, so what remains is a second one, or an internal edge. One index\n // for both shapes and the event axis, so the fleet is read once. A consumer\n // document the index could not read or place refuses too (./refusal.ts):\n // \"nobody could look\" never let a removal through before the one index.\n const still = async (svc: string, op: string): Promise<StillNamed> => stillNamed(await consumers.opConsumers(svc, op));\n // The landscape merge only ADDS edges, so an edge is dropped or redrawn in the living map itself.\n const route = `drop or redraw its edge in the architecture/ project, MODIFY or REMOVE the requirement that names it in this feature's specs/<service>/ delta`;\n for (const [svc, retiring] of removingOps) {\n for (const op of retiring) {\n const found = await still(svc, op);\n if (nothingStill(found)) continue;\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-op-consumed\",\n subject: svc,\n details: refusalDetails(found),\n message: refusalMessage(found, {\n subject: svc,\n lost: `this feature retires '${op}'`,\n verb: \"consume\",\n named: (list) => `${svc}: this feature retires '${op}', but the fleet would still consume it after this merge — ${list}. Retire each consumer in the same change (${route}), or archive with --approve to break it deliberately.`,\n }),\n });\n }\n }\n\n // The same loss with no marker to read: another operation defined where a\n // consumed one lives replaces it there, and once the contract the merge\n // would write defines it nowhere it has left the contract exactly as a\n // retirement does. One code, because the breach and its override are the\n // removal's — only `api.op-unconsumed` (advisory) said anything before, and\n // the archive passed without `--approve`. With no marker there is no REMOVED\n // requirement either, so the provider's own requirement naming the old id\n // stays unless this feature restates it — `own` is what catches it.\n for (const [svc, displaced] of displacedOps) {\n for (const { id: op, at } of displaced) {\n const found = await still(svc, op);\n if (nothingStill(found)) continue;\n const by = at.map(({ address, by: id }) => `${id === undefined ? \"an operation with no operationId\" : `'${id}'`} at ${address}`).join(\" and \");\n const lost = `this feature's openapi.yaml defines ${by}, where '${op}' lives now — the merge replaces it there and would leave '${op}' defined nowhere`;\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-op-consumed\",\n subject: svc,\n details: refusalDetails(found),\n message: refusalMessage(found, {\n subject: svc,\n lost,\n verb: \"consume\",\n named: (list) => `${svc}: ${lost}, but it is still consumed — ${list}. Define '${op}' at another address in this delta if it is to stay. To take it out on purpose, update every consumer in the same change — ${route} (${svc}'s own restated to name the new operation, for a rename; a REMOVED one owes no x-loam-remove marker, since the overwrite already takes '${op}' out) — or archive with --approve to break it deliberately.`,\n }),\n });\n }\n }\n return issues;\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { documentDoubt } from "../../consumers/scan.js";
2
+ /**
3
+ * The gate's reading of one answer: `victims` and `own` as evidence, and of the
4
+ * suspensions only the document doubts. A caller drawn with no op and a
5
+ * contested hop stay out — `documentDoubt` (core/consumers/scan.ts) says why
6
+ * they are a warning's worth in `loam diff` and nothing here.
7
+ */
8
+ export function stillNamed(found) {
9
+ return {
10
+ named: [...found.victims, ...found.own],
11
+ unchecked: [...found.suspended, ...found.ownSuspended].filter(documentDoubt).map((s) => s.text),
12
+ };
13
+ }
14
+ /** Nothing named and nothing unchecked: the removal is the gate's to let through. */
15
+ export function nothingStill(still) {
16
+ return still.named.length === 0 && still.unchecked.length === 0;
17
+ }
18
+ /** The one remedy every unchecked document shares, after "make". */
19
+ const READABLE = "each document listed readable (re-save it as UTF-8, or leave one directory per service id) so the gate can answer";
20
+ /**
21
+ * The message for one refusal. With named consumers it is the gate's own
22
+ * sentence, plus one more when documents went unchecked too; with only
23
+ * unchecked documents it never claims a consumer — "nobody could look" is not
24
+ * "somebody still calls it", any more than it is "nobody does".
25
+ */
26
+ export function refusalMessage(still, wording) {
27
+ const unchecked = still.unchecked.join("; ");
28
+ if (still.named.length === 0) {
29
+ return `${wording.subject}: ${wording.lost}, but whether the fleet would still ${wording.verb} it after this merge could not be checked — ${unchecked}. Make ${READABLE}, or archive with --approve to break it deliberately.`;
30
+ }
31
+ const named = wording.named(still.named.join("; "));
32
+ return still.unchecked.length === 0 ? named : `${named} Whether anything else would still ${wording.verb} it could not be checked either — ${unchecked}: make ${READABLE}.`;
33
+ }
34
+ /** What a refusal's `details[]` lists: the named consumers, then the documents nobody could check. */
35
+ export function refusalDetails(still) {
36
+ return [...still.named, ...still.unchecked];
37
+ }
38
+ //# sourceMappingURL=refusal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refusal.js","sourceRoot":"","sources":["../../../../src/core/coherence/consumed/refusal.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAUxD;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAgB;IACzC,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC;QACvC,SAAS,EAAE,CAAC,GAAG,KAAK,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KAChG,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC;AAClE,CAAC;AAaD,oEAAoE;AACpE,MAAM,QAAQ,GAAG,mHAAmH,CAAC;AAErI;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAiB,EAAE,OAAuB;IACvE,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,IAAI,uCAAuC,OAAO,CAAC,IAAI,+CAA+C,SAAS,UAAU,QAAQ,uDAAuD,CAAC;IACjO,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,sCAAsC,OAAO,CAAC,IAAI,qCAAqC,SAAS,UAAU,QAAQ,GAAG,CAAC;AAC9K,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,cAAc,CAAC,KAAiB;IAC9C,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;AAC9C,CAAC","sourcesContent":["/**\n * What a removal refusal counts of one consumer-index answer, and how it says\n * so — shared by the operation gate (./operations.ts) and its event twin\n * (../events/events.ts), so a consumer nobody could read refuses the same way,\n * in the same sentence, on both axes.\n *\n * Two lists, because they are two different claims. `named` is positive\n * evidence — the merge would strand these — and its sentence is each gate's\n * own, with the remedy that fits the shape. `unchecked` is a document nobody\n * could read or place, so the refusal must not say the fleet still consumes\n * anything: it says the gate could not tell, names the documents, and gives the\n * one remedy they share. Both land in `details[]`, the named ones first — the\n * order `loam diff` lists victims and doubts in.\n */\nimport type { Consumers } from \"../../consumers/consumers.js\";\nimport { documentDoubt } from \"../../consumers/scan.js\";\n\n/** What the gate counts of one index answer. */\nexport interface StillNamed {\n /** The consumers the merge would strand, then the provider's own documents still naming it. */\n named: string[];\n /** The documents — a consumer's, or the provider's own — the scan could not read or place (`documentDoubt`). */\n unchecked: string[];\n}\n\n/**\n * The gate's reading of one answer: `victims` and `own` as evidence, and of the\n * suspensions only the document doubts. A caller drawn with no op and a\n * contested hop stay out — `documentDoubt` (core/consumers/scan.ts) says why\n * they are a warning's worth in `loam diff` and nothing here.\n */\nexport function stillNamed(found: Consumers): StillNamed {\n return {\n named: [...found.victims, ...found.own],\n unchecked: [...found.suspended, ...found.ownSuspended].filter(documentDoubt).map((s) => s.text),\n };\n}\n\n/** Nothing named and nothing unchecked: the removal is the gate's to let through. */\nexport function nothingStill(still: StillNamed): boolean {\n return still.named.length === 0 && still.unchecked.length === 0;\n}\n\n/** One refusal's sentence, as the gate that raises it phrases it. */\nexport interface RefusalWording {\n subject: string;\n /** How this feature's merge takes it out — the opening clause, e.g. \"this feature retires 'capturePayment'\". */\n lost: string;\n /** What a consumer does with it: \"consume\" for an operation, \"name\" for a message. */\n verb: string;\n /** The gate's own sentence over its named consumers, given them joined. */\n named: (list: string) => string;\n}\n\n/** The one remedy every unchecked document shares, after \"make\". */\nconst READABLE = \"each document listed readable (re-save it as UTF-8, or leave one directory per service id) so the gate can answer\";\n\n/**\n * The message for one refusal. With named consumers it is the gate's own\n * sentence, plus one more when documents went unchecked too; with only\n * unchecked documents it never claims a consumer — \"nobody could look\" is not\n * \"somebody still calls it\", any more than it is \"nobody does\".\n */\nexport function refusalMessage(still: StillNamed, wording: RefusalWording): string {\n const unchecked = still.unchecked.join(\"; \");\n if (still.named.length === 0) {\n return `${wording.subject}: ${wording.lost}, but whether the fleet would still ${wording.verb} it after this merge could not be checked — ${unchecked}. Make ${READABLE}, or archive with --approve to break it deliberately.`;\n }\n const named = wording.named(still.named.join(\"; \"));\n return still.unchecked.length === 0 ? named : `${named} Whether anything else would still ${wording.verb} it could not be checked either — ${unchecked}: make ${READABLE}.`;\n}\n\n/** What a refusal's `details[]` lists: the named consumers, then the documents nobody could check. */\nexport function refusalDetails(still: StillNamed): string[] {\n return [...still.named, ...still.unchecked];\n}\n"]}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `health.uncovered`, predicted: the alert or SLI a living `arch.spec.md`
3
+ * requirement covers today that this feature's arch delta would leave covered
4
+ * by nothing.
5
+ *
6
+ * Validate's service target grades `health.yaml` against the LIVING
7
+ * `arch.spec.md` (`commands/validate/service/specs.ts`), and nothing graded the
8
+ * delta that rewrites it. A MODIFIED requirement restates its whole block, so
9
+ * a `Covers:` line the author did not carry over is a line the merge deletes:
10
+ * the feature gate called the delta clean, the archive printed "living spec +
11
+ * landscape are now complete + current.", and the next `validate --all` warned
12
+ * that the signal now ships with nothing testing it (the real-fleet review,
13
+ * 2026-10-21).
14
+ *
15
+ * ONLY WHAT THIS MERGE TAKES AWAY. A signal the living spec already leaves
16
+ * uncovered is the service target's warning today and stays its warning; this
17
+ * gate reporting it too would file somebody else's debt against a feature
18
+ * that never touched it. And a service this feature creates has no living
19
+ * `health.yaml` — a feature cannot carry one — so it has nothing to lose.
20
+ *
21
+ * A WARNING, the grade validate gives the same fact: the documents are legal
22
+ * and the merge is correct, so it advises and never gates. The merge is the
23
+ * archive's own (`afterMerge` over `applyRequirementDelta`), so the
24
+ * requirements this reads are the ones the archive would write; a delta the
25
+ * merge refuses is read as living ∪ ADDED ∪ MODIFIED, which can only find MORE
26
+ * coverage — the quiet direction for an advisory.
27
+ */
28
+ import { existsSync } from "node:fs";
29
+ import { parseCoversEntry } from "../../c4/arch.js";
30
+ import { afterMerge } from "../../consumers/after.js";
31
+ import { parseRequirements, readRequirementsDocument } from "../../document/parse.js";
32
+ import { featureSpecPaths } from "../../repo/paths.js";
33
+ import { locateServicePaths } from "../../repo/service-target.js";
34
+ import { readHealth } from "../../vocabulary/health.js";
35
+ /** The requirements naming `form:id` in a `Covers:` line — the rule `coversEntries` applies, REMOVED governing nothing. */
36
+ function covering(reqs, form, id) {
37
+ return reqs.filter((r) => r.kind !== "REMOVED" &&
38
+ r.covers.some((raw) => {
39
+ const entry = parseCoversEntry(raw);
40
+ return entry.form === form && entry.id === id;
41
+ }));
42
+ }
43
+ export async function droppedHealthCoverage(scope) {
44
+ const { docsDir, featureDir, context } = scope;
45
+ const read = async (path) => context === undefined ? parseRequirements(await readRequirementsDocument(path)) : await context.readRequirements(path);
46
+ const issues = [];
47
+ for (const service of scope.services) {
48
+ const deltaPath = featureSpecPaths(featureDir, service).archSpec;
49
+ if (!existsSync(deltaPath))
50
+ continue;
51
+ const paths = await locateServicePaths(docsDir, service, context);
52
+ if (!existsSync(paths.archSpec) || !existsSync(paths.health))
53
+ continue;
54
+ // An unreadable health.yaml is `health.invalid`, the service target's
55
+ // finding; there are no ids here to grade either way.
56
+ const health = await readHealth(paths.health);
57
+ if (health.unreadable)
58
+ continue;
59
+ const living = await read(paths.archSpec);
60
+ const after = afterMerge(living, await read(deltaPath)).map((m) => m.requirement);
61
+ for (const { form, ids } of [
62
+ { form: "alert", ids: health.ids.alerts },
63
+ { form: "sli", ids: health.ids.slis },
64
+ ]) {
65
+ for (const id of ids) {
66
+ const before = covering(living, form, id);
67
+ if (before.length === 0 || covering(after, form, id).length > 0)
68
+ continue;
69
+ const signal = form === "alert" ? "alert" : "SLI";
70
+ issues.push({
71
+ severity: "warn",
72
+ code: "health.uncovered",
73
+ subject: service,
74
+ message: `${service}: health.yaml declares ${signal} '${id}', which the living arch.spec.md covers today ` +
75
+ `(${before.map((r) => `'${r.name}'`).join(", ")}) and nothing would after this feature's merge — its ` +
76
+ `arch.spec.md delta rewrites or removes every requirement carrying 'Covers: ${form}:${id}', so the next ` +
77
+ `\`loam validate --all\` reports health.uncovered. Keep the entry on the MODIFIED requirement, or cover ` +
78
+ `the signal from another requirement in the delta`,
79
+ });
80
+ }
81
+ }
82
+ }
83
+ return issues;
84
+ }
85
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../../src/core/coherence/covers/health.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `health.uncovered`, predicted: the alert or SLI a living `arch.spec.md`\n * requirement covers today that this feature's arch delta would leave covered\n * by nothing.\n *\n * Validate's service target grades `health.yaml` against the LIVING\n * `arch.spec.md` (`commands/validate/service/specs.ts`), and nothing graded the\n * delta that rewrites it. A MODIFIED requirement restates its whole block, so\n * a `Covers:` line the author did not carry over is a line the merge deletes:\n * the feature gate called the delta clean, the archive printed \"living spec +\n * landscape are now complete + current.\", and the next `validate --all` warned\n * that the signal now ships with nothing testing it (the real-fleet review,\n * 2026-10-21).\n *\n * ONLY WHAT THIS MERGE TAKES AWAY. A signal the living spec already leaves\n * uncovered is the service target's warning today and stays its warning; this\n * gate reporting it too would file somebody else's debt against a feature\n * that never touched it. And a service this feature creates has no living\n * `health.yaml` — a feature cannot carry one — so it has nothing to lose.\n *\n * A WARNING, the grade validate gives the same fact: the documents are legal\n * and the merge is correct, so it advises and never gates. The merge is the\n * archive's own (`afterMerge` over `applyRequirementDelta`), so the\n * requirements this reads are the ones the archive would write; a delta the\n * merge refuses is read as living ∪ ADDED ∪ MODIFIED, which can only find MORE\n * coverage — the quiet direction for an advisory.\n */\nimport { existsSync } from \"node:fs\";\nimport { parseCoversEntry } from \"../../c4/arch.js\";\nimport { afterMerge } from \"../../consumers/after.js\";\nimport { parseRequirements, readRequirementsDocument } from \"../../document/parse.js\";\nimport type { Requirement } from \"../../document/spec.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport type { PathableService } from \"../../kernel/ids/service.js\";\nimport { featureSpecPaths } from \"../../repo/paths.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { readHealth } from \"../../vocabulary/health.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\n\n/** The feature being graded, and the services its `specs/` touch. */\nexport interface HealthCoverageScope {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n services: readonly PathableService[];\n context?: FleetContext;\n}\n\n/** The requirements naming `form:id` in a `Covers:` line — the rule `coversEntries` applies, REMOVED governing nothing. */\nfunction covering(reqs: readonly Requirement[], form: \"alert\" | \"sli\", id: string): Requirement[] {\n return reqs.filter(\n (r) =>\n r.kind !== \"REMOVED\" &&\n r.covers.some((raw) => {\n const entry = parseCoversEntry(raw);\n return entry.form === form && entry.id === id;\n }),\n );\n}\n\nexport async function droppedHealthCoverage(scope: HealthCoverageScope): Promise<Issue[]> {\n const { docsDir, featureDir, context } = scope;\n const read = async (path: string): Promise<Requirement[]> =>\n context === undefined ? parseRequirements(await readRequirementsDocument(path)) : await context.readRequirements(path);\n const issues: Issue[] = [];\n for (const service of scope.services) {\n const deltaPath = featureSpecPaths(featureDir, service).archSpec;\n if (!existsSync(deltaPath)) continue;\n const paths = await locateServicePaths(docsDir, service, context);\n if (!existsSync(paths.archSpec) || !existsSync(paths.health)) continue;\n // An unreadable health.yaml is `health.invalid`, the service target's\n // finding; there are no ids here to grade either way.\n const health = await readHealth(paths.health);\n if (health.unreadable) continue;\n const living = await read(paths.archSpec);\n const after = afterMerge(living, await read(deltaPath)).map((m) => m.requirement);\n for (const { form, ids } of [\n { form: \"alert\" as const, ids: health.ids.alerts },\n { form: \"sli\" as const, ids: health.ids.slis },\n ]) {\n for (const id of ids) {\n const before = covering(living, form, id);\n if (before.length === 0 || covering(after, form, id).length > 0) continue;\n const signal = form === \"alert\" ? \"alert\" : \"SLI\";\n issues.push({\n severity: \"warn\",\n code: \"health.uncovered\",\n subject: service,\n message:\n `${service}: health.yaml declares ${signal} '${id}', which the living arch.spec.md covers today ` +\n `(${before.map((r) => `'${r.name}'`).join(\", \")}) and nothing would after this feature's merge — its ` +\n `arch.spec.md delta rewrites or removes every requirement carrying 'Covers: ${form}:${id}', so the next ` +\n `\\`loam validate --all\\` reports health.uncovered. Keep the entry on the MODIFIED requirement, or cover ` +\n `the signal from another requirement in the delta`,\n });\n }\n }\n }\n return issues;\n}\n"]}