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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,287 @@
1
+ import { existsSync } from "node:fs";
2
+ import { loadFile } from "../c4/likec4.js";
3
+ import { elementService, serviceResolver } from "../c4/resolve/service.js";
4
+ import { serviceIdProblem } from "../kernel/ids/service.js";
5
+ import { deltaShapeIssues } from "../delta/delta.js";
6
+ import { featurePaths } from "../repo/paths.js";
7
+ import { featureSpecServices } from "../repo/repo.js";
8
+ import { enumeratedServiceIds, enumeratedServiceIndex } from "../repo/service-target.js";
9
+ import { serviceOperationIds } from "../openapi/doc.js";
10
+ import { declaredByService } from "./declared.js";
11
+ import { coherenceLookups } from "./lookups.js";
12
+ import { eventCoherence } from "./events/events.js";
13
+ import { authoringIssues } from "./authoring/scaffold.js";
14
+ import { glossaryDeltaIssues } from "../glossary/delta.js";
15
+ import { flowDeltaIssues } from "../usecases/delta/flows.js";
16
+ import { deploymentDeltaIssues, readFeatureDeployments } from "../deployment/delta.js";
17
+ export async function featureCoherence(request) {
18
+ const { docsDir, featureDir, featureId, preloadedDelta, context } = request;
19
+ const scope = { docsDir, featureDir, featureId };
20
+ // Delta shape first: a diff that does not apply to the living spec explains
21
+ // everything downstream, and it is the one breach that is silent without a check.
22
+ const issues = await deltaShapeIssues(docsDir, featureDir, featureId, context);
23
+ // The vocabulary axis, and it is asked first because it is cheap and
24
+ // independent: one `existsSync` for a feature with no glossary/, and nothing
25
+ // downstream reads its answer. A term this feature introduces that the living
26
+ // glossary already defines is the one thing that can go wrong on a create-only
27
+ // merge, and it must be caught before the copy, not after.
28
+ issues.push(...(await glossaryDeltaIssues(docsDir, featureDir)));
29
+ // The use-case axis, and it is asked here for the vocabulary axis's reasons
30
+ // exactly: one walk over a directory that is usually absent, nothing
31
+ // downstream reads its answer, and a create-only merge has one thing that can
32
+ // go wrong. A flow this feature introduces that the living `architecture/`
33
+ // already holds must be caught before the copy, not after it has replaced an
34
+ // authored hop sequence.
35
+ issues.push(...(await flowDeltaIssues(docsDir, featureDir)));
36
+ issues.push(...(await deploymentDeltaIssues(docsDir, featureDir)));
37
+ // And the topology's second question, which the collision check above cannot
38
+ // ask: does the document READ against the map this feature's own merge would
39
+ // leave behind. It matters more literally here than one axis over — a feature
40
+ // that stands a service up in a new cluster declares that service in its
41
+ // `delta.likec4` and instances it in the topology, so the element the document
42
+ // names exists only post-merge — and without it the archive copies an
43
+ // unreadable document into `architecture/` for the next reader's
44
+ // `loam validate --all` to fail on, against a file they did not write.
45
+ const topology = await readFeatureDeployments({
46
+ docsDir,
47
+ featureDir,
48
+ featureId,
49
+ // A function rather than the context itself, for the reason the use-case
50
+ // overlay states: one memoised read does not justify an edge from
51
+ // `core/deployment/` to `fleet-context`.
52
+ ...(context === undefined ? {} : { load: (path) => context.loadLikeC4(path) }),
53
+ });
54
+ if (topology.kind === "unreadable") {
55
+ issues.push({
56
+ severity: "error",
57
+ code: "deployment.doc-invalid",
58
+ subject: featureId,
59
+ // The opening sentence does not say whose file is at fault, on the
60
+ // `usecase.flow-invalid` message's reasoning: the corpus is the feature's
61
+ // `deployment/` PLUS the living `architecture/` documents it extends, so
62
+ // the failing path can be a living one — a landscape that no longer
63
+ // parses, a topology somebody else wrote. Every message carries its own
64
+ // path, so a reader can place it.
65
+ message: `this feature's deployment document(s) could not be read against the map its own merge would leave behind, ` +
66
+ `so the archive would land an architecture/ nothing has checked. The corpus is the feature's deployment/ AND ` +
67
+ `the living architecture/ documents it extends, so the fault may be in either — each message names its file. ` +
68
+ `${topology.errors.length} problem(s): ${topology.errors.join("; ")}`,
69
+ });
70
+ }
71
+ // --- C4 delta ---
72
+ let elements = [];
73
+ let taggedEls = [];
74
+ let taggedRels = [];
75
+ const deltaPath = featurePaths(featureDir).delta;
76
+ if (existsSync(deltaPath)) {
77
+ const res = preloadedDelta ?? (context === undefined ? await loadFile(deltaPath) : await context.loadLikeC4(deltaPath));
78
+ if (res.errors.length > 0) {
79
+ // An unreadable architecture axis can prove nothing — it must never count as coherent.
80
+ issues.push({
81
+ severity: "error",
82
+ code: "delta.invalid",
83
+ message: `delta.likec4 has ${res.errors.length} parse error(s) — architecture axis unreadable (run \`loam validate --feature ${featureId}\`)`,
84
+ });
85
+ }
86
+ else {
87
+ elements = res.elements;
88
+ taggedEls = res.elements.filter((e) => e.tags.includes(featureId));
89
+ taggedRels = res.relationships.filter((r) => r.tags.includes(featureId));
90
+ // Every rule below filters by the feature tag, so a delta whose author
91
+ // forgot the tags entirely passes every check and archives while merging
92
+ // nothing. Whole-file only: once ANYTHING is tagged, deciding that an
93
+ // untagged edge "looks intended" would be guessing.
94
+ if ((res.elements.length > 0 || res.relationships.length > 0) &&
95
+ taggedEls.length === 0 &&
96
+ taggedRels.length === 0) {
97
+ issues.push({
98
+ severity: "error",
99
+ code: "delta.nothing-tagged",
100
+ message: `delta.likec4 declares elements/relationships but none are tagged #${featureId}; loam cannot see untagged changes`,
101
+ });
102
+ }
103
+ }
104
+ }
105
+ // --- per-service specs (requirement operations) + openapi deltas ---
106
+ const svcNames = await featureSpecServices(featureDir, context);
107
+ // Every axis below joins on the service id, so endpoints resolve through the
108
+ // element's `metadata { service }` binding — never through what the box is
109
+ // called. The enumerated fleet — `services/` plus this feature's own
110
+ // `specs/` directories, the same union `enumeratedTarget` probes below —
111
+ // rides into the resolver so a delta that models CONTAINERS joins correctly:
112
+ // without it, an edge into `payment.api` resolves to a service called "api"
113
+ // that has never existed, and every check below grades the edge against an
114
+ // absent service instead of the one that owns the container. An unenumerable
115
+ // services/ falls back to the feature's own names — every living probe would
116
+ // have found nothing there anyway.
117
+ const fleetIds = await enumeratedServiceIds(docsDir, context);
118
+ const svcOf = serviceResolver(elements, new Set([...fleetIds, ...svcNames]));
119
+ // Which elements the landscape merge would SPLICE: the tagged ones and
120
+ // everything nested inside their blocks. Defined here because two checks
121
+ // share it — the scaffold gate just below, and the binding-grammar check
122
+ // near the end of this function — and the merge carries a tagged element's
123
+ // authored block over byte for byte, children included, so a scope narrower
124
+ // than the splice lets a nested child's text ride through unread.
125
+ const taggedIds = taggedEls.map((el) => el.id);
126
+ const ridesWithTag = (el) => el.tags.includes(featureId) || taggedIds.some((id) => el.id.startsWith(`${id}.`));
127
+ // The scaffold gate: placeholder text and an unwritten intent are legal
128
+ // documents whose merge would publish content nobody authored — the exact
129
+ // template strings `loam new` wrote (authoring/scaffold.ts owns the list).
130
+ issues.push(...(await authoringIssues({ featureDir, featureId, splicedEls: elements.filter(ridesWithTag), svcNames, context })));
131
+ const { reqOps, removingOps, featureApiOps, unreadableApis, } = await declaredByService(scope, svcNames, issues, context);
132
+ const { governedByLivingSpec, edgeConsumers, requirementConsumers, definedElsewhere, deprecatedInLiving, undeprecatedByFeature, } = coherenceLookups(scope, context);
133
+ for (const [svc, retiring] of removingOps) {
134
+ for (const op of retiring) {
135
+ const consumers = [...(await edgeConsumers(svc, op)), ...(await requirementConsumers(svc, op))];
136
+ if (consumers.length === 0)
137
+ continue;
138
+ issues.push({
139
+ severity: "error",
140
+ code: "openapi.remove-op-consumed",
141
+ subject: svc,
142
+ 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.`,
143
+ });
144
+ }
145
+ }
146
+ // E1: Spec -> API — every operation a requirement governs must exist in that service's OpenAPI.
147
+ for (const [svc, ops] of reqOps) {
148
+ // The union below reads the feature's own contract; unreadable means the
149
+ // op may well be defined in the file nobody could open, and `op-undefined`
150
+ // would point the author at the requirement when the truth is the YAML.
151
+ // `openapi.invalid` (declared.ts) already gates; one breach, one finding.
152
+ if (unreadableApis.has(svc))
153
+ continue;
154
+ const available = await serviceOperationIds(docsDir, svc, featureDir, context);
155
+ for (const op of ops) {
156
+ if (available.includes(op))
157
+ continue;
158
+ const other = await definedElsewhere(svc, op);
159
+ if (other !== undefined) {
160
+ issues.push({ severity: "warn", code: "spec-api.op-pending", message: `requirement in ${svc} governs '${op}', defined by in-flight ${other} — archive it first` });
161
+ continue;
162
+ }
163
+ issues.push({ severity: "error", code: "spec-api.op-undefined", message: `requirement in ${svc} governs '${op}', not defined in ${svc}'s OpenAPI` });
164
+ }
165
+ }
166
+ // The declared→enumerated bridge for edge TARGETS. `svcOf` answers with
167
+ // whatever the document wrote — `metadata { service '../../x' }` parses in
168
+ // LikeC4 without one error — and every living lookup below joins its answer
169
+ // into `services/<target>/` or `specs/<target>/`. So a target resolves
170
+ // through the enumerations first (the fleet's `services/` plus this
171
+ // feature's own `specs/`, where a service it introduces lives), and a name
172
+ // neither knows skips the lookups — no ops, not governed, not deprecated —
173
+ // grading exactly as an absent service, spelled with the declared name.
174
+ const enumeratedTarget = enumeratedServiceIndex(docsDir, svcNames, context);
175
+ // E2 / W1 / W4: C4 edges vs API + requirements.
176
+ for (const r of taggedRels) {
177
+ if (r.op === undefined) {
178
+ if ((r.title ?? "").toLowerCase().startsWith("call")) {
179
+ 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 })` });
180
+ }
181
+ continue;
182
+ }
183
+ const target = svcOf(r.target);
184
+ const pathable = await enumeratedTarget(target);
185
+ // Same suspension as E1, but only for the verdicts that READ a contract:
186
+ // does the op exist, is it being removed, is it deprecated (whose
187
+ // un-deprecation escape reads the feature contract too). Each of those
188
+ // would be an opinion about a file nobody could open. The governance
189
+ // question below is deliberately NOT suspended — it joins the edge against
190
+ // requirement documents alone, so the broken YAML could never change its
191
+ // answer, and silencing it deferred a real warning for no reason.
192
+ const contractUnreadable = pathable !== undefined && unreadableApis.has(pathable);
193
+ const available = pathable === undefined || contractUnreadable
194
+ ? []
195
+ : await serviceOperationIds(docsDir, pathable, featureDir, context);
196
+ if (contractUnreadable) {
197
+ // openapi.invalid (declared.ts) already gates; one breach, one finding.
198
+ }
199
+ else if (pathable !== undefined && removingOps.get(pathable)?.has(r.op) === true) {
200
+ 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}` });
201
+ }
202
+ else if (!available.includes(r.op)) {
203
+ const other = await definedElsewhere(target, r.op);
204
+ if (other !== undefined) {
205
+ 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` });
206
+ }
207
+ else {
208
+ 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)` });
209
+ }
210
+ }
211
+ // Governed by THIS target's requirements, not by anybody's. An operationId
212
+ // is unique only inside one contract, so the fleet-wide set this used to
213
+ // ask meant a `getStatus` governed in service A silenced the question for
214
+ // service B's entirely unrelated `getStatus` — the warning went quiet for
215
+ // the edge that needed it, and only in repos where some other service
216
+ // happened to reuse the name. The living half on the same line has always
217
+ // joined per service (`governedByLivingSpec(target, …)`); this is the delta
218
+ // half asking it the same way.
219
+ if (pathable === undefined || (!(reqOps.get(pathable) ?? []).includes(r.op) && !(await governedByLivingSpec(pathable, r.op)))) {
220
+ issues.push({ severity: "warn", code: "c4.op-ungoverned", message: `'${r.op}' is called by ${svcOf(r.source)} but no requirement governs it` });
221
+ }
222
+ // Lifecycle: this NEW tagged edge builds consumption on an operation the
223
+ // living provider contract already marks deprecated. Advisory, never an
224
+ // archive gate — the edge is legal and the contract holds — but new
225
+ // consumption of a dying op deserves an eye before it ships. Quiet when
226
+ // the feature's own openapi delta drops the flag: that state is the fix
227
+ // in progress, not new consumption of a dying op.
228
+ if (!contractUnreadable && pathable !== undefined && (await deprecatedInLiving(pathable, r.op)) && !(await undeprecatedByFeature(pathable, r.op))) {
229
+ 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` });
230
+ }
231
+ }
232
+ // W2: API -> C4 — every operation the feature adds should be consumed by some edge.
233
+ const consumed = new Set(taggedRels.map((r) => r.op).filter((o) => o !== undefined));
234
+ for (const op of featureApiOps) {
235
+ if (!consumed.has(op)) {
236
+ issues.push({ severity: "warn", code: "api.op-unconsumed", message: `operation '${op}' is added but no architecture edge consumes it (provider-only or unmodeled)` });
237
+ }
238
+ }
239
+ // W3: a new service should carry a requirement delta.
240
+ for (const e of taggedEls) {
241
+ const svc = elementService(e);
242
+ if (e.kind === "softwareSystem" && !svcNames.some((n) => n === svc)) {
243
+ issues.push({ severity: "warn", code: "service.no-requirement-delta", message: `new service ${svc} has no requirement delta under specs/` });
244
+ }
245
+ }
246
+ // An explicit `metadata { service '<id>' }` binding, held to the same grammar
247
+ // every `--service` flag is — on the tagged elements AND everything nested
248
+ // inside their blocks. The scope is the splice's, not the tag's: the
249
+ // landscape merge carries a tagged element's authored block over byte for
250
+ // byte, children included (landscape-merge.ts's rides() exists exactly so a
251
+ // child travels inside its parent's text), so an untagged container's
252
+ // binding reaches the living map as surely as its tagged parent's — and
253
+ // checking only the tagged elements themselves let it ride through unread.
254
+ // The binding is the one name the archive TRUSTS: the merge splices it into
255
+ // the living map verbatim, and the new-service scan probes
256
+ // `services/<binding>/` with it — where `metadata { service '../outside-svc' }`,
257
+ // which LikeC4 parses without a murmur, collapses the probe right out of
258
+ // services/, and the spliced map fails the very next `validate --all`
259
+ // (`landscape.binding-unknown`). Explicit bindings only, on purpose: a prose
260
+ // title with no binding is legal C4, and a title becomes a path only through
261
+ // `specs/<svc>/`, whose own grammar check (`delta.service-id-invalid`)
262
+ // guards that route. `ridesWithTag` itself is defined up beside the scaffold
263
+ // gate, which shares the splice's scope for the same reason.
264
+ for (const e of elements.filter(ridesWithTag)) {
265
+ if (e.service === undefined)
266
+ continue;
267
+ const problem = serviceIdProblem(e.service, "metadata { service } binding");
268
+ if (problem === null)
269
+ continue;
270
+ issues.push({
271
+ severity: "error",
272
+ code: "c4.service-binding-invalid",
273
+ subject: e.service,
274
+ message: `'${e.title}' (${e.id}) — ${problem} ` +
275
+ `\`loam archive\` would splice '${e.service}' into the living landscape.likec4 exactly as written, ` +
276
+ `and probe services/${e.service}/ with it. Bind the element to the services/<id>/ directory it means.`,
277
+ });
278
+ }
279
+ // The event axis — the same three-way agreement the operation checks above
280
+ // grade, joined on the message name instead of the operationId: the
281
+ // feature's asyncapi deltas (pins, markers, justification), the tagged
282
+ // edges' publishes/consumes metadata, and the requirement deltas'
283
+ // Publishes:/Consumes: lines (./events/events.ts owns the walk).
284
+ issues.push(...(await eventCoherence({ scope, svcNames, taggedRels, svcOf, context })));
285
+ return issues;
286
+ }
287
+ //# sourceMappingURL=coherence.js.map
@@ -0,0 +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 { deploymentDeltaIssues, readFeatureDeployments } from \"../deployment/delta.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 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 });\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 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"]}
@@ -0,0 +1,269 @@
1
+ /**
2
+ * What a feature's own delta DECLARES, per service, indexed once.
3
+ *
4
+ * Seven indexes over the same four files (the delta spec.md, arch.spec.md,
5
+ * openapi.yaml, and the living contract they are graded against), built in one
6
+ * walk because every check downstream joins on the service id and would
7
+ * otherwise re-read the delta per question. The distinctions here are the ones
8
+ * the checks cannot make for themselves: an operation a REMOVED requirement
9
+ * governs is being retired, so it neither claims the contract nor governs
10
+ * anything after the merge; a removal marker that names an id also defined
11
+ * elsewhere in the delta is a RELOCATION, not a retirement; and a marker with
12
+ * no `operationId` at all is a shape only this walk can see. The capability
13
+ * gate rides the same loop: both delta spec documents are graded against the
14
+ * fleet vocabulary here, because coherence.ts and this package's file count
15
+ * are both at their limits and the walk already holds the parsed documents.
16
+ */
17
+ import { existsSync } from "node:fs";
18
+ import { NotUtf8DocumentError } from "../kernel/document-bytes.js";
19
+ import { readFile } from "node:fs/promises";
20
+ import {} from "../kernel/ids/service.js";
21
+ import { featureSpecPaths } from "../repo/paths.js";
22
+ import { locateServicePaths } from "../repo/service-target.js";
23
+ import { parseRequirements } from "../document/parse.js";
24
+ import { readCapabilityVocabulary } from "../capabilities/capabilities.js";
25
+ import { capabilityRequirementIndex } from "../capabilities/findings.js";
26
+ import { withFeatureCapabilities, withFeatureRequirements } from "../capabilities/delta/overlay.js";
27
+ import { featureCapabilityDeltas } from "../capabilities/delta/tree.js";
28
+ import { realizesUnknownIssues } from "../capabilities/realizes/findings.js";
29
+ import { capabilityUnknownIssues } from "../capabilities/findings.js";
30
+ import { openapiBaselineIssues } from "../openapi/baseline/gate.js";
31
+ import { readOpenapi } from "../openapi/doc.js";
32
+ export async function declaredByService(scope, svcNames, issues, context) {
33
+ const { docsDir, featureDir, featureId } = scope;
34
+ const reqOps = new Map();
35
+ const removedReqOps = new Map();
36
+ /** Per service: operations this feature genuinely retires (markers minus relocations). */
37
+ const removingOps = new Map();
38
+ /** Per service: every id a removal marker names, relocations included. */
39
+ const allMarkerIds = new Map();
40
+ /** Per service: does the feature contract carry a marker with no operationId? */
41
+ const anonymousMarkers = new Map();
42
+ const featureApiOps = new Set();
43
+ const unreadableApis = new Set();
44
+ // The capability gate's vocabulary, once per call. Absent or invalid stays
45
+ // SILENT here — capability.invalid is validate --all's one finding about a
46
+ // broken file, and an absent file means the fleet never opted in — so with
47
+ // either, nothing new gates archive (the roadmap's own trade; the
48
+ // /loam-check row tells agents to fix the YAML first). `grading` is that
49
+ // silence hoisted into a guard, so a silent axis also skips the reads that
50
+ // exist only to feed it.
51
+ //
52
+ // The vocabulary is the one this feature's MERGE would leave behind, not
53
+ // the one on disk: a feature that adds a capability requirement in its own
54
+ // capability delta and `Realizes:` it from its own service delta is the
55
+ // flow the whole axis exists for, and graded against the living tree alone
56
+ // it is refused with `capability.realizes-unknown` — an error, gating
57
+ // archive, for a target the same archive creates. `capabilities/delta/
58
+ // overlay.ts` states the whole judgement. One `existsSync` for a feature
59
+ // that carries no capability delta, which is every feature in a fleet that
60
+ // has not adopted the axis.
61
+ const capabilityDeltas = context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);
62
+ const capabilityVocab = withFeatureCapabilities(context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir), capabilityDeltas.docs.map((d) => d.id));
63
+ const grading = capabilityVocab.present && capabilityVocab.invalid === undefined;
64
+ // The `Realizes:` index, built at most ONCE per feature and only if some
65
+ // delta document actually reaches the grading branch. Lazy rather than
66
+ // eager because it reads every capability document in the fleet, and a
67
+ // feature whose deltas carry no requirement file at all must not pay for
68
+ // that — the same "a silent axis skips the reads that feed it" rule
69
+ // `grading` above states, one level deeper.
70
+ let pendingIndex = null;
71
+ const capabilityReqs = () => {
72
+ const read = context === undefined
73
+ ? async (p) => parseRequirements(await readFile(p, "utf8"))
74
+ : (p) => context.readRequirements(p);
75
+ // The living index first, then widened by what this feature's capability
76
+ // deltas ADD or MODIFY. Both halves are inside the lazy thunk, so a
77
+ // feature whose deltas carry no requirement file at all pays for neither.
78
+ pendingIndex ??= capabilityRequirementIndex(capabilityVocab, read).then(async (index) => withFeatureRequirements(index, await Promise.all(capabilityDeltas.docs.map(async (d) => ({ id: d.id, reqs: await read(d.spec) })))));
79
+ return pendingIndex;
80
+ };
81
+ for (const svc of svcNames) {
82
+ const paths = featureSpecPaths(featureDir, svc);
83
+ if (existsSync(paths.spec)) {
84
+ const reqs = context === undefined
85
+ ? parseRequirements(await readFile(paths.spec, "utf8"))
86
+ : await context.readRequirements(paths.spec);
87
+ // REMOVED requirements are being retired along with their operations — their
88
+ // ops neither claim the contract (E1) nor govern anything after the merge.
89
+ reqOps.set(svc, reqs.filter((r) => r.kind !== "REMOVED").flatMap((r) => r.operations));
90
+ removedReqOps.set(svc, new Set(reqs.filter((r) => r.kind === "REMOVED").flatMap((r) => r.operations)));
91
+ if (grading) {
92
+ const target = { where: `${svc}: spec.md`, subject: svc };
93
+ issues.push(...capabilityUnknownIssues(reqs, target, capabilityVocab));
94
+ issues.push(...realizesUnknownIssues(reqs, target, await capabilityReqs()));
95
+ }
96
+ }
97
+ // The delta arch.spec.md carries the same grammar and merges the same
98
+ // way, so an undeclared capability in it gates identically. Nothing else
99
+ // in this walk needs the arch delta, so the read itself sits behind
100
+ // `grading` — and it is NOT always cached: `validate --feature` passes a
101
+ // context, but archive's plan gate (commands/archive/plan/gate.ts) calls
102
+ // featureCoherence without one, so this parse is paid fresh there.
103
+ if (grading && existsSync(paths.archSpec)) {
104
+ const archReqs = context === undefined
105
+ ? parseRequirements(await readFile(paths.archSpec, "utf8"))
106
+ : await context.readRequirements(paths.archSpec);
107
+ const archTarget = { where: `${svc}: arch.spec.md`, subject: svc };
108
+ issues.push(...capabilityUnknownIssues(archReqs, archTarget, capabilityVocab));
109
+ issues.push(...realizesUnknownIssues(archReqs, archTarget, await capabilityReqs()));
110
+ }
111
+ // Only operations genuinely NEW to this service count as feature-added: authors
112
+ // restate the full living API in the delta file (it is a complete document, not a patch).
113
+ const featDoc = await readOpenapi(paths.openapi, context);
114
+ // A feature contract that EXISTS but does not read is a broken document,
115
+ // not an empty one — the living-side `openapi.invalid` discipline
116
+ // (validate/service/api.ts), applied to the delta. Before this check,
117
+ // `validate --feature` said nothing about the file at all: the empty
118
+ // parse silently skipped every baseline pin and removal marker in it,
119
+ // and E1 graded requirements against a contract that was never read.
120
+ // The rest of this service's contract-axis checks are suspended — every
121
+ // one of them would be an opinion about a document nobody could open.
122
+ if (featDoc.unreadable) {
123
+ unreadableApis.add(svc);
124
+ issues.push({
125
+ severity: "error",
126
+ code: "openapi.invalid",
127
+ subject: svc,
128
+ message: `${svc}: this feature's openapi.yaml does not parse${featDoc.error === undefined ? "" : ` (${featDoc.error})`} — the contract axis is unchecked and the merge would have nothing true to write. Fix the YAML first.`,
129
+ });
130
+ continue;
131
+ }
132
+ const featOps = featDoc.ops;
133
+ const removals = featOps.filter((op) => op.remove);
134
+ // A relocation — same operationId, removal marker on the old slot, upsert
135
+ // on the new one — retires nothing. Every rule that asks "is this operation
136
+ // going away" must therefore ask the NET set (markers this feature does not
137
+ // redefine), while "did the author write a marker at all" asks the raw one.
138
+ // Conflating them made moving an endpoint fail three checks at once for a
139
+ // change that removes nothing.
140
+ const redefined = new Set(featOps.filter((op) => !op.remove).map((op) => op.id));
141
+ const markerIds = new Set(removals.map((op) => op.id));
142
+ const netRemoved = new Set([...markerIds].filter((id) => !redefined.has(id)));
143
+ removingOps.set(svc, netRemoved);
144
+ allMarkerIds.set(svc, markerIds);
145
+ anonymousMarkers.set(svc, featDoc.anonymousRemovals.length > 0);
146
+ // A marker with no operationId names a slot but no operation. Every
147
+ // id-keyed check is blind to it, so it used to travel all the way into the
148
+ // living contract as a literal `x-loam-remove: true` — the one feature-only
149
+ // key that must never be published.
150
+ for (const marker of featDoc.anonymousRemovals) {
151
+ issues.push({
152
+ severity: "error",
153
+ code: "openapi.remove-marker-anonymous",
154
+ subject: svc,
155
+ message: `${svc}: ${marker.method} ${marker.path} carries x-loam-remove: true but declares no operationId — loam cannot tell which operation it retires; name the operation the living contract has at that slot`,
156
+ });
157
+ }
158
+ const livingDoc = await readOpenapi((await locateServicePaths(docsDir, svc, context)).openapi, context);
159
+ const livingOps = livingDoc.ops;
160
+ for (const id of livingDoc.duplicateIds) {
161
+ const slots = livingOps.filter((op) => op.id === id).map((op) => `${op.method} ${op.path}`);
162
+ issues.push({
163
+ severity: "warn",
164
+ code: "openapi.duplicate-operationid",
165
+ subject: svc,
166
+ message: `${svc}: the living OpenAPI defines operationId '${id}' at ${slots.join(" and ")} — every join on the id (a requirement's Operations: line, an edge's metadata { op }, a removal marker) picks one of those slots arbitrarily`,
167
+ });
168
+ }
169
+ // The baseline axis — operation pins, path-item keys and components —
170
+ // graded in its own package (openapi/baseline/gate.ts), slot-keyed
171
+ // exactly as the merge writes. Texts come through the context's memoized
172
+ // read when there is one, mirroring the readRequirements pattern above;
173
+ // the gate itself never touches the filesystem.
174
+ if (existsSync(paths.openapi)) {
175
+ const livingOpenapiPath = (await locateServicePaths(docsDir, svc, context)).openapi;
176
+ // `context.readText` DECODES and THROWS on non-UTF-8 bytes, where the
177
+ // no-context `readFile` substitutes U+FFFD and lets `readOpenapi`
178
+ // flag the document unreadable. Uncaught, one service's UTF-16 living
179
+ // contract took down the whole feature target under validate's
180
+ // context while archive's no-context path sailed past — the exact
181
+ // context/no-context divergence doc.ts documents as paid for once.
182
+ // Either text failing to decode gets the same answer the gate gives
183
+ // an unreadable parse: that side's surface checks are skipped.
184
+ const readOr = async (p) => {
185
+ try {
186
+ return context === undefined ? await readFile(p, "utf8") : await context.readText(p);
187
+ }
188
+ catch (err) {
189
+ if (err instanceof NotUtf8DocumentError)
190
+ return undefined;
191
+ throw err;
192
+ }
193
+ };
194
+ const featureText = await readOr(paths.openapi);
195
+ const livingText = !existsSync(livingOpenapiPath) ? undefined : await readOr(livingOpenapiPath);
196
+ if (featureText !== undefined) {
197
+ issues.push(...openapiBaselineIssues({ featDoc, livingDoc, featureText, livingText, service: svc, featureId }));
198
+ }
199
+ }
200
+ if (featOps.length > 0) {
201
+ const living = new Set(livingOps.filter((op) => !op.remove).map((op) => op.id));
202
+ for (const op of featOps) {
203
+ if (!op.remove && !living.has(op.id))
204
+ featureApiOps.add(op.id);
205
+ }
206
+ // Removal is exact: the feature names both an operationId and the
207
+ // path+method slot it expects to delete. That catches stale deltas whose
208
+ // target moved or was already retired before the archive planner runs.
209
+ const justified = removedReqOps.get(svc) ?? new Set();
210
+ for (const marker of removals) {
211
+ const target = livingOps.find((op) => op.path === marker.path && op.method === marker.method);
212
+ if (target === undefined) {
213
+ issues.push({
214
+ severity: "error",
215
+ code: "openapi.remove-target-missing",
216
+ subject: svc,
217
+ message: `${svc}: removal marker for '${marker.id}' addresses ${marker.method} ${marker.path}, but no living operation exists there`,
218
+ });
219
+ }
220
+ else if (target.id !== marker.id) {
221
+ issues.push({
222
+ severity: "error",
223
+ code: "openapi.remove-target-mismatch",
224
+ subject: svc,
225
+ message: `${svc}: removal marker names '${marker.id}' at ${marker.method} ${marker.path}, but the living operation there is '${target.id}'`,
226
+ });
227
+ }
228
+ // A relocation needs no REMOVED requirement: the requirement governing
229
+ // the operation stays, the operation only changes address.
230
+ if (!netRemoved.has(marker.id))
231
+ continue;
232
+ if (!justified.has(marker.id)) {
233
+ issues.push({
234
+ severity: "error",
235
+ code: "openapi.remove-marker-unjustified",
236
+ subject: svc,
237
+ message: `${svc}: removal marker for '${marker.id}' is not governed by a REMOVED requirement's Operations: line`,
238
+ });
239
+ }
240
+ }
241
+ }
242
+ }
243
+ for (const [svc, required] of removedReqOps) {
244
+ // The markers live in the unreadable file — "no matching marker" would
245
+ // be a claim about a document nobody could open.
246
+ if (unreadableApis.has(svc))
247
+ continue;
248
+ const marked = allMarkerIds.get(svc) ?? new Set();
249
+ for (const op of required) {
250
+ if (marked.has(op))
251
+ continue;
252
+ // Distinguish "no marker at all" from "a marker is there but loam cannot
253
+ // read which operation it names" — the first asks the author to write the
254
+ // marker, the second to write the operationId, and telling somebody who
255
+ // already wrote the marker that there is none sends them looking for a
256
+ // file they are staring at.
257
+ issues.push({
258
+ severity: "error",
259
+ code: "openapi.remove-marker-missing",
260
+ subject: svc,
261
+ message: anonymousMarkers.get(svc) === true
262
+ ? `${svc}: REMOVED requirement governs '${op}', and its feature openapi.yaml carries an x-loam-remove: true marker with no operationId — name '${op}' on that marker`
263
+ : `${svc}: REMOVED requirement governs '${op}', but its feature openapi.yaml has no matching x-loam-remove: true marker`,
264
+ });
265
+ }
266
+ }
267
+ return { reqOps, removedReqOps, removingOps, allMarkerIds, anonymousMarkers, featureApiOps, unreadableApis };
268
+ }
269
+ //# sourceMappingURL=declared.js.map