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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"delta.js","sourceRoot":"","sources":["../../../src/core/delta/delta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Does the diff apply to the thing it claims to change?\n *\n * A requirement delta is a diff against a living spec, and nothing used to check\n * that it lands. Every failure mode here is silent today: MODIFIED of a\n * requirement that does not exist is merged as a creation, ADDED of one that\n * does exist REPLACES it (scenarios and all) while the author believes they are\n * adding, and a heading that nearly matches the delta grammar parses as plain\n * prose so archive merges nothing at all and says nothing about it.\n *\n * The last of those has a legal-looking cousin: a requirement under an ordinary\n * prose heading (`## Behavior`, `## Error Handling`) is BASE too, and BASE never\n * merges. Upstream OpenSpec deltas are written that way, so the answer is to name\n * what will be lost rather than to start merging prose sections.\n *\n * These run inside the archive gate, because the merge is where the damage lands.\n *\n * TWO CORPORA, ONE ALGEBRA. A feature's delta documents live in two places —\n * `specs/<svc>/{spec,arch.spec}.md` against the service tree, and\n * `capabilities/<id>/spec.md` against the authored business tree — and every\n * grade about a DOCUMENT and its living counterpart applies to both verbatim,\n * with the capability id in `subject` where a service id would be. That is not\n * a coincidence to exploit but the reason the business corpus was given deltas\n * at all: a capability document is a requirements document, so the diff that\n * changes one has exactly the same ways of not landing. A second implementation\n * would be a second set of answers to `delta.added-duplicate`.\n *\n * The exception is the four CROSS-FEATURE warnings — `delta.added-conflict`,\n * `delta.modified-conflict`, `delta.modified-pending`, `delta.removed-pending`\n * — which ask what OTHER features in flight claim. `./claims.ts` indexes the\n * service corpus only and states why; on the business axis those four are\n * silent, which is a missing warning and never a wrong answer.\n *\n * This module is only the walk: which documents get read, in what order, and how\n * each is handed a `DeltaScope`. The checks themselves are in `./document.ts`\n * (a document against itself) and `./select.ts` (the delta against the living\n * text). Their order is load-bearing and stays here, where it can be read in one\n * screen.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport { type FleetContext } from \"../fleet-context.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport { type Requirement } from \"../document/spec.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../repo/paths.js\";\nimport { featureUseCasesDir, livingCapabilityPaths } from \"../repo/authored/paths.js\";\nimport { readCapabilityVocabulary } from \"../capabilities/capabilities.js\";\n\nimport { flowCoverage, type FlowPromises } from \"../usecases/delta/claims.js\";\nimport { enumeratedServiceIds, locateServicePaths } from \"../repo/service-target.js\";\nimport { featureSpecServices } from \"../repo/repo.js\";\nimport { capabilityDocIssues } from \"../capabilities/delta/doc.js\";\nimport { featureCapabilityDeltas } from \"../capabilities/delta/tree.js\";\nimport {\n removedRealizedIssues,\n uncoveredIssues,\n type CapabilityDeltaDoc,\n type RealizingDoc,\n} from \"../capabilities/delta/uncovered.js\";\nimport { type Issue } from \"../vocabulary/issue.js\";\nimport { claimLookup, type ClaimLookup } from \"./claims.js\";\nimport { deltaDocumentIssues, livingDocumentIssues } from \"./document.js\";\nimport { indexLiving, type DeltaScope, type LivingIndex } from \"./scope.js\";\nimport { selectionIssues } from \"./select.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * The business corpus's ONE axis. A capability document carries the promise and\n * nothing else — there is no `arch.spec.md` beside it, because \"how it is\n * built\" is precisely the altitude a capability requirement may not describe\n * (`capability.requirement-service-scoped` is that rule). It has no `key`\n * because it indexes no `ServicePaths`, which is why `DeltaScope.axis` is the\n * narrower `DeltaAxis` and not `SpecAxis`.\n */\nconst CAPABILITY_AXIS = { file: \"spec.md\" };\n\n/**\n * What every document in one feature's walk shares.\n *\n * A record rather than two more parameters because both values are about the\n * WALK and not about the document being graded — `gradeDelta` would otherwise\n * read as a four-argument function whose last two arguments never vary within\n * a run, which is how the next value gets added as a fifth.\n */\ninterface DeltaReads {\n /** What OTHER features in flight claim — lazy, so the common case never scans. */\n claims: ClaimLookup;\n /** The invocation's read index, when the caller threaded one. */\n context?: FleetContext;\n}\n\nexport async function deltaShapeIssues(\n docsDir: DocsDir,\n featureDir: FeatureDir,\n featureId: string,\n context?: FleetContext,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n const services = await featureSpecServices(featureDir, context);\n // One `existsSync` for a feature that carries no capability delta, which is\n // every feature in a fleet that has not adopted the business axis.\n const capabilities =\n context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);\n // The flows this feature brings, and one `existsSync` for a feature that\n // brings none — which is every feature in a fleet that has not adopted the\n // axis. The directory, not the documents: what is needed here is whether to\n // pay for the overlay at all, and the walk that lists them happens once,\n // inside it.\n const hasFlows = existsSync(featureUseCasesDir(featureDir));\n // The early return must ask about EVERY corpus. Asking only about services\n // meant a capability-only feature — a business change with no service touched\n // yet, which is exactly what an analyst writes first — was graded by nothing\n // and archived whatever its delta said; asking only about those two would do\n // the same to a feature whose whole content is a cross-service flow, which is\n // the shape an architect answers an analyst with.\n if (services.length === 0 && capabilities.docs.length === 0 && !hasFlows) return issues;\n\n // What OTHER features in flight claim. Only built when a claim has to be\n // checked — the common case never pays for the scan.\n const reads: DeltaReads = {\n claims: claimLookup(docsDir, featureId, context),\n ...(context === undefined ? {} : { context }),\n };\n\n // The two corpora's parsed requirements, kept as the walk produces them so\n // the `Realizes:` join below can be taken without re-reading a byte. That\n // join is the one question neither corpus can answer alone — which service\n // requirement keeps which business promise — and this is the only place in\n // loam where both sides are already open (`capabilities/delta/uncovered.ts`\n // holds the rules; nothing here decides anything).\n const serviceDeltas: RealizingDoc[] = [];\n const capabilityDeltas: CapabilityDeltaDoc[] = [];\n\n // Both requirement-carrying files per service run the same checks — one code\n // path parameterized by filename, the merge's own factoring. `where` names\n // the arch file in messages so a finding cannot be chased into the wrong\n // document; spec.md keeps its historical spelling.\n for (const service of services) {\n for (const axis of SPEC_AXES) {\n const specPath = featureSpecPaths(featureDir, service)[axis.key];\n if (!existsSync(specPath)) continue;\n const scope: DeltaScope = {\n kind: \"service\",\n subject: service,\n axis,\n featureId,\n docsDir,\n specPath,\n where: axis.key === \"spec\" ? service : `${service} (arch.spec.md)`,\n // How the axis's living document is named in messages — spec.md keeps\n // the historical \"living spec\", the arch axis says which file it means.\n livingDoc: axis.key === \"spec\" ? \"living spec\" : \"living arch.spec.md\",\n };\n const graded = await gradeDelta(scope, (await locateServicePaths(docsDir, service, context))[axis.key], reads);\n issues.push(...graded.issues);\n serviceDeltas.push({ service, file: axis.file, reqs: graded.reqs });\n }\n }\n\n for (const doc of capabilities.docs) {\n const scope: DeltaScope = {\n kind: \"capability\",\n subject: doc.id,\n axis: CAPABILITY_AXIS,\n featureId,\n docsDir,\n specPath: doc.spec,\n // `capability <id>`, not `capabilities/<id>` — the second reads as the\n // LIVING directory, and every message this labels is about the FEATURE's\n // delta of it. Sending an author to edit the wrong one of two files with\n // the same name is the whole reason `where` and `livingDoc` are separate.\n where: `capability ${doc.id}`,\n livingDoc: `living capabilities/${doc.id}/spec.md`,\n };\n // Absent is a real and ordinary answer on this axis: the first feature to\n // mention a capability is what creates its living document, so the whole\n // delta grades against `[]` and every requirement in it is legitimately an\n // addition.\n const graded = await gradeDelta(scope, livingCapabilityPaths(docsDir, doc.id).spec, reads);\n issues.push(...graded.issues);\n // The document's own three rules, on the requirements this delta would\n // MERGE — before the merge, where the author can still fix them. See\n // `capabilities/delta/doc.ts` for why grading only the living copy leaves a\n // hole straight through the altitude rule.\n issues.push(\n ...capabilityDocIssues(graded.reqs, {\n where: relative(docsDir, doc.spec).split(/[\\\\/]/).join(\"/\"),\n subject: doc.id,\n }),\n );\n capabilityDeltas.push({ id: doc.id, reqs: graded.reqs, living: graded.living.all });\n }\n\n // The other carrier of a promise, read once and only when this feature\n // actually brings a flow: the `dynamic view`s under `features/<FEAT>/usecases/`,\n // parsed over the map this feature's own merge would leave behind\n // (`core/usecases/delta/overlay.ts`).\n // A feature with no `usecases/` directory is a REAL empty answer, not an\n // ungraded one: there is no flow, so \"no flow keeps this\" is true and the\n // message may say it.\n const kept: FlowPromises = hasFlows\n ? await flowCoverage({\n docsDir,\n featureDir,\n featureId,\n capabilities: capabilityDeltas,\n // Read HERE, through the invocation's index, because this module holds\n // it and `core/usecases/` does not — every other read in this walk does\n // the same, and `core/repo/service-target.ts` prices a fleet\n // enumeration per call at ~13 ms on a 120-service fleet.\n vocabulary: context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir),\n known: new Set(await enumeratedServiceIds(docsDir, context)),\n // A function rather than the context itself: one memoised LikeC4 read\n // does not justify an edge from `core/usecases/` to `fleet-context`, and\n // the edge would push that package up a DAG level for nothing.\n ...(context === undefined ? {} : { load: (path: string) => context.loadLikeC4(path) }),\n })\n : { kind: \"read\", kept: [] };\n if (kept.kind === \"unreadable\") {\n // ERROR, and the archive gate's own `NEVER_OVERRIDABLE` keeps `--approve`\n // off it. This is the refusal that stops a flow being copied into\n // `architecture/` and the NEXT reader's `loam validate --all` carrying the\n // failure — including the case the overlay exists for, a hop naming an\n // element the merge does not land. Mechanical, so it is not loam's judgement\n // to override.\n issues.push({\n severity: \"error\",\n code: \"usecase.flow-invalid\",\n subject: featureId,\n // The opening sentence does NOT say whose file is at fault, and that is\n // deliberate: the corpus this reads is the feature's flows PLUS the living\n // `architecture/` documents they are drawn over, so the failing path can\n // be a living one — a landscape that no longer parses, a use case somebody\n // else wrote. Every message carries its own path, so a reader can place\n // it; an opening line that blamed `usecases/` would send them to the wrong\n // file first.\n message:\n `this feature's flows could not be graded against the map its own merge would leave behind, so the archive ` +\n `would land an architecture/ nothing has checked. The corpus is the feature's usecases/ AND the living ` +\n `architecture/ documents they are drawn over, so the fault may be in either — each message names its file. ` +\n `${kept.errors.length} problem(s): ${kept.errors.join(\"; \")}`,\n });\n }\n\n // The `Realizes:` join, both directions, over documents this walk has already\n // parsed. Skipped entirely when the feature carries no capability delta,\n // which is every feature in a fleet that has not adopted the business axis —\n // and the removal half asks for the living corpus only when something is\n // actually retired, so even an adopting fleet pays the fleet-wide read on the\n // features that earn it.\n if (capabilityDeltas.length > 0) {\n // The three arms travel as three, and flattening two of them to `[]` is\n // the one thing this must not do: `capability.uncovered` says in words that\n // no flow keeps the promise, so handing it an empty list for a corpus loam\n // could not read or could not resolve makes it assert a negative it never\n // evaluated — and gate on it. `claims.ts`'s header states the rule; this is\n // where a caller could break it.\n issues.push(\n ...uncoveredIssues(\n capabilityDeltas,\n serviceDeltas,\n kept.kind === \"read\"\n ? { graded: true, kept: kept.kept }\n : { graded: false, why: kept.kind === \"ungraded\" ? kept.why : kept.errors.join(\"; \") },\n ),\n );\n issues.push(\n ...(await removedRealizedIssues({\n capabilities: capabilityDeltas,\n deltas: serviceDeltas,\n living: () => livingRealizers(docsDir, context),\n })),\n );\n }\n\n return issues;\n}\n\n/**\n * Every LIVING service requirements document in the fleet, for the one question\n * that has to ask about services this feature never mentions: does anything out\n * there still realize a capability requirement this feature retires?\n *\n * The same shape and the same cost `openapi.remove-op-consumed` pays through\n * `coherence/lookups.ts` one axis over, and for the same reason — the merge\n * deletes the promise while the `Realizes:` line stays, so the very next\n * `validate --all` reports a breach on a repository whose author was never in\n * this feature. Called behind a thunk (`removedRealizedIssues` invokes it only\n * when something is retired), so a feature that removes nothing never runs it.\n */\nasync function livingRealizers(docsDir: DocsDir, context?: FleetContext): Promise<RealizingDoc[]> {\n const out: RealizingDoc[] = [];\n for (const service of await enumeratedServiceIds(docsDir, context)) {\n const paths = await locateServicePaths(docsDir, service, context);\n for (const axis of SPEC_AXES) {\n const path = paths[axis.key];\n if (!existsSync(path)) continue;\n const reqs =\n context === undefined ? parseRequirements(await readFile(path, \"utf8\")) : await context.readRequirements(path);\n out.push({ service, file: axis.file, reqs });\n }\n }\n return out;\n}\n\n/**\n * One delta document against one living document: the three passes, in the\n * order that makes their messages readable.\n *\n * The order is load-bearing. `deltaDocumentIssues` settles what the delta says\n * about itself, `livingDocumentIssues` settles whether the living text can be\n * selected in at all, and only then does `selectionIssues` compare them — a pin\n * the document pass already refused must not also be reported stale, which\n * would send its author to `loam rebase` for a problem rebase does not fix.\n *\n * The parsed delta requirements come back with the issues because the capability\n * corpus grades them a second way (`capabilityDocIssues`) and re-reading the\n * file to do it would be a second parse of bytes already in hand — and, without\n * a context, a second chance for the two passes to disagree about the document.\n * The LIVING index rides back for the same reason once more: the removal\n * direction of the `Realizes:` join has to resolve a REMOVED spelled by heading\n * into the id it retires, and that id is a fact about the living document.\n */\nasync function gradeDelta(\n scope: DeltaScope,\n livingPath: string,\n reads: DeltaReads,\n): Promise<{ issues: Issue[]; reqs: Requirement[]; living: LivingIndex }> {\n const { claims, context } = reads;\n const raw = context === undefined ? await readFile(scope.specPath, \"utf8\") : await context.readText(scope.specPath);\n const reqs = context === undefined ? parseRequirements(raw) : await context.readRequirements(scope.specPath);\n const living = indexLiving(\n !existsSync(livingPath)\n ? []\n : context === undefined\n ? parseRequirements(await readFile(livingPath, \"utf8\"))\n : await context.readRequirements(livingPath),\n );\n return {\n reqs,\n living,\n issues: [\n ...deltaDocumentIssues(scope, raw, reqs),\n ...livingDocumentIssues(scope, living),\n ...(await selectionIssues(scope, reqs, living, claims)),\n ],\n };\n}\n"]}
1
+ {"version":3,"file":"delta.js","sourceRoot":"","sources":["../../../src/core/delta/delta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Does the diff apply to the thing it claims to change?\n *\n * A requirement delta is a diff against a living spec, and nothing used to check\n * that it lands. Every failure mode here is silent today: MODIFIED of a\n * requirement that does not exist is merged as a creation, ADDED of one that\n * does exist REPLACES it (scenarios and all) while the author believes they are\n * adding, and a heading that nearly matches the delta grammar parses as plain\n * prose so archive merges nothing at all and says nothing about it.\n *\n * The last of those has a legal-looking cousin: a requirement under an ordinary\n * prose heading (`## Behavior`, `## Error Handling`) is BASE too, and BASE never\n * merges. Upstream OpenSpec deltas are written that way, so the answer is to name\n * what will be lost rather than to start merging prose sections.\n *\n * These run inside the archive gate, because the merge is where the damage lands.\n *\n * TWO CORPORA, ONE ALGEBRA. A feature's delta documents live in two places —\n * `specs/<svc>/{spec,arch.spec}.md` against the service tree, and\n * `capabilities/<id>/spec.md` against the authored business tree — and every\n * grade about a DOCUMENT and its living counterpart applies to both verbatim,\n * with the capability id in `subject` where a service id would be. That is not\n * a coincidence to exploit but the reason the business corpus was given deltas\n * at all: a capability document is a requirements document, so the diff that\n * changes one has exactly the same ways of not landing. A second implementation\n * would be a second set of answers to `delta.added-duplicate`.\n *\n * The exception is the four CROSS-FEATURE warnings — `delta.added-conflict`,\n * `delta.modified-conflict`, `delta.modified-pending`, `delta.removed-pending`\n * — which ask what OTHER features in flight claim. `./claims.ts` indexes the\n * service corpus only and states why; on the business axis those four are\n * silent, which is a missing warning and never a wrong answer.\n *\n * This module is only the walk: which documents get read, in what order, and how\n * each is handed a `DeltaScope`. The checks themselves are in `./document.ts`\n * (a document against itself) and `./select.ts` (the delta against the living\n * text). Their order is load-bearing and stays here, where it can be read in one\n * screen.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport { FleetContext } from \"../fleet-context.js\";\nimport { parseRequirements, readRequirementsDocument } from \"../document/parse.js\";\nimport { type Requirement } from \"../document/spec.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../repo/paths.js\";\nimport { featureUseCasesDir, livingCapabilityPaths } from \"../repo/authored/paths.js\";\nimport { readCapabilityVocabulary } from \"../capabilities/capabilities.js\";\nimport { extendingModels } from \"../c4/service-model/fleet/extending.js\";\n\nimport { flowCoverage, type FlowPromises } from \"../usecases/delta/claims.js\";\nimport { changedPromiseFlowIssues, type ServiceDeltaPair } from \"../usecases/delta/modified.js\";\nimport { enumeratedServiceIds, locateServicePaths } from \"../repo/service-target.js\";\nimport { featureSpecServices } from \"../repo/repo.js\";\nimport { capabilityDocIssues } from \"../capabilities/delta/doc.js\";\nimport { featureCapabilityDeltas } from \"../capabilities/delta/tree.js\";\nimport {\n removedRealizedIssues,\n uncoveredIssues,\n type CapabilityDeltaDoc,\n type RealizingDoc,\n} from \"../capabilities/delta/uncovered.js\";\nimport { type Issue } from \"../vocabulary/issue.js\";\nimport { claimLookup, type ClaimLookup } from \"./claims.js\";\nimport { deltaDocumentIssues, livingDocumentIssues } from \"./document.js\";\nimport { indexLiving, type DeltaScope, type LivingIndex } from \"./scope.js\";\nimport { selectionIssues } from \"./select.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * The business corpus's ONE axis. A capability document carries the promise and\n * nothing else — there is no `arch.spec.md` beside it, because \"how it is\n * built\" is precisely the altitude a capability requirement may not describe\n * (`capability.requirement-service-scoped` is that rule). It has no `key`\n * because it indexes no `ServicePaths`, which is why `DeltaScope.axis` is the\n * narrower `DeltaAxis` and not `SpecAxis`.\n */\nconst CAPABILITY_AXIS = { file: \"spec.md\" };\n\n/**\n * What every document in one feature's walk shares.\n *\n * A record rather than two more parameters because both values are about the\n * WALK and not about the document being graded — `gradeDelta` would otherwise\n * read as a four-argument function whose last two arguments never vary within\n * a run, which is how the next value gets added as a fifth.\n */\ninterface DeltaReads {\n /** What OTHER features in flight claim — lazy, so the common case never scans. */\n claims: ClaimLookup;\n /** The invocation's read index, when the caller threaded one. */\n context?: FleetContext;\n}\n\nexport async function deltaShapeIssues(\n docsDir: DocsDir,\n featureDir: FeatureDir,\n featureId: string,\n context?: FleetContext,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n const services = await featureSpecServices(featureDir, context);\n // One `existsSync` for a feature that carries no capability delta, which is\n // every feature in a fleet that has not adopted the business axis.\n const capabilities =\n context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);\n // The flows this feature brings, and one `existsSync` for a feature that\n // brings none — which is every feature in a fleet that has not adopted the\n // axis. The directory, not the documents: what is needed here is whether to\n // pay for the overlay at all, and the walk that lists them happens once,\n // inside it.\n const hasFlows = existsSync(featureUseCasesDir(featureDir));\n // The early return must ask about EVERY corpus. Asking only about services\n // meant a capability-only feature — a business change with no service touched\n // yet, which is exactly what an analyst writes first — was graded by nothing\n // and archived whatever its delta said; asking only about those two would do\n // the same to a feature whose whole content is a cross-service flow, which is\n // the shape an architect answers an analyst with.\n if (services.length === 0 && capabilities.docs.length === 0 && !hasFlows) return issues;\n\n // What OTHER features in flight claim. Only built when a claim has to be\n // checked — the common case never pays for the scan.\n const reads: DeltaReads = {\n claims: claimLookup(docsDir, featureId, context),\n ...(context === undefined ? {} : { context }),\n };\n\n // The two corpora's parsed requirements, kept as the walk produces them so\n // the `Realizes:` join below can be taken without re-reading a byte. That\n // join is the one question neither corpus can answer alone — which service\n // requirement keeps which business promise — and this is the only place in\n // loam where both sides are already open (`capabilities/delta/uncovered.ts`\n // holds the rules; nothing here decides anything).\n const serviceDeltas: RealizingDoc[] = [];\n const capabilityDeltas: CapabilityDeltaDoc[] = [];\n // Each service axis beside its living document, for the one question that\n // needs both: which living flow claims a requirement this feature changes.\n const servicePairs: ServiceDeltaPair[] = [];\n\n // Both requirement-carrying files per service run the same checks — one code\n // path parameterized by filename, the merge's own factoring. `where` names\n // the arch file in messages so a finding cannot be chased into the wrong\n // document; spec.md keeps its historical spelling.\n for (const service of services) {\n for (const axis of SPEC_AXES) {\n const specPath = featureSpecPaths(featureDir, service)[axis.key];\n if (!existsSync(specPath)) continue;\n const scope: DeltaScope = {\n kind: \"service\",\n subject: service,\n axis,\n featureId,\n docsDir,\n specPath,\n where: axis.key === \"spec\" ? service : `${service} (arch.spec.md)`,\n // How the axis's living document is named in messages — spec.md keeps\n // the historical \"living spec\", the arch axis says which file it means.\n livingDoc: axis.key === \"spec\" ? \"living spec\" : \"living arch.spec.md\",\n };\n const graded = await gradeDelta(scope, (await locateServicePaths(docsDir, service, context))[axis.key], reads);\n issues.push(...graded.issues);\n serviceDeltas.push({ service, file: axis.file, reqs: graded.reqs });\n const paths = (): ReturnType<typeof locateServicePaths> => locateServicePaths(docsDir, service, context);\n servicePairs.push({ id: service, axis: axis.key, reqs: graded.reqs, living: graded.living.all, paths });\n }\n }\n\n for (const doc of capabilities.docs) {\n const scope: DeltaScope = {\n kind: \"capability\",\n subject: doc.id,\n axis: CAPABILITY_AXIS,\n featureId,\n docsDir,\n specPath: doc.spec,\n // `capability <id>`, not `capabilities/<id>` — the second reads as the\n // LIVING directory, and every message this labels is about the FEATURE's\n // delta of it. Sending an author to edit the wrong one of two files with\n // the same name is the whole reason `where` and `livingDoc` are separate.\n where: `capability ${doc.id}`,\n livingDoc: `living capabilities/${doc.id}/spec.md`,\n };\n // Absent is a real and ordinary answer on this axis: the first feature to\n // mention a capability is what creates its living document, so the whole\n // delta grades against `[]` and every requirement in it is legitimately an\n // addition.\n const graded = await gradeDelta(scope, livingCapabilityPaths(docsDir, doc.id).spec, reads);\n issues.push(...graded.issues);\n // The document's own three rules, on the requirements this delta would\n // MERGE — before the merge, where the author can still fix them. See\n // `capabilities/delta/doc.ts` for why grading only the living copy leaves a\n // hole straight through the altitude rule.\n issues.push(\n ...capabilityDocIssues(graded.reqs, {\n where: relative(docsDir, doc.spec).split(/[\\\\/]/).join(\"/\"),\n subject: doc.id,\n }),\n );\n capabilityDeltas.push({ id: doc.id, reqs: graded.reqs, living: graded.living.all });\n }\n\n // The other carrier of a promise, read once and only when this feature\n // actually brings a flow: the `dynamic view`s under `features/<FEAT>/usecases/`,\n // parsed over the map this feature's own merge would leave behind\n // (`core/usecases/delta/overlay.ts`).\n // A feature with no `usecases/` directory is a REAL empty answer, not an\n // ungraded one: there is no flow, so \"no flow keeps this\" is true and the\n // message may say it.\n const flows: { promises: FlowPromises; issues: Issue[] } = hasFlows\n ? await flowCoverage({\n docsDir,\n featureDir,\n featureId,\n capabilities: capabilityDeltas,\n // Read HERE, through the invocation's index, because this module holds\n // it and `core/usecases/` does not — every other read in this walk does\n // the same, and `core/repo/service-target.ts` prices a fleet\n // enumeration per call at ~13 ms on a 120-service fleet.\n vocabulary: context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir),\n // The fleet the merge leaves: each `specs/<svc>/` of this feature becomes a\n // `services/<svc>/`, so a hop into a service it introduces resolves to that\n // service in the grade, as it will under the next `validate --all`.\n known: new Set([...(await enumeratedServiceIds(docsDir, context)), ...services]),\n // Decoded or refused by name (`readRequirementsDocument`), never `readFile`'s\n // utf8 — a UTF-16 capability document would read as no requirements and\n // convict every flow naming it.\n read: async (path) => (context === undefined ? parseRequirements(await readRequirementsDocument(path)) : await context.readRequirements(path)),\n // A function rather than the context itself: one memoised LikeC4 read\n // does not justify an edge from `core/usecases/` to `fleet-context`, and\n // the edge would push that package up a DAG level for nothing.\n ...(context === undefined ? {} : { load: (path: string) => context.loadLikeC4(path) }),\n // The fleet's extending models, for the merge preview the overlay\n // grades against — read HERE for `vocabulary`'s reason exactly, and as\n // a thunk so the walk pays for it only when a flow is really there.\n // Without it the preview is the pre-routing map and `usecase.flow-invalid`\n // fails open on the one shape it exists for: a hop into\n // `<service-fqn>.<container>`, which the archive routes into that\n // service's own model (verification 2026-09-04, review F8).\n models: () => extendingModels(docsDir, context),\n })\n : { promises: { kind: \"read\", kept: [] }, issues: [] };\n const kept = flows.promises;\n if (kept.kind === \"unreadable\") {\n // ERROR, and the archive gate's own `NEVER_OVERRIDABLE` keeps `--approve`\n // off it. This is the refusal that stops a flow being copied into\n // `architecture/` and the NEXT reader's `loam validate --all` carrying the\n // failure — including the case the overlay exists for, a hop naming an\n // element the merge does not land. Mechanical, so it is not loam's judgement\n // to override.\n issues.push({\n severity: \"error\",\n code: \"usecase.flow-invalid\",\n subject: featureId,\n // The opening sentence does NOT say whose file is at fault, and that is\n // deliberate: the corpus this reads is the feature's flows PLUS the living\n // `architecture/` documents they are drawn over, so the failing path can\n // be a living one — a landscape that no longer parses, a use case somebody\n // else wrote. Every message carries its own path, so a reader can place\n // it; an opening line that blamed `usecases/` would send them to the wrong\n // file first.\n message:\n `this feature's flows could not be graded against the map its own merge would leave behind, so the archive ` +\n `would land an architecture/ nothing has checked. The corpus is the feature's usecases/ AND the living ` +\n `architecture/ documents they are drawn over, so the fault may be in either — each message names its file. ` +\n `${kept.errors.length} problem(s): ${kept.errors.join(\"; \")}`,\n });\n }\n // And the flows that DID read, graded as `validate --all` grades them once\n // living: every hop against the post-merge map, every `#cap-`/`#req-` tag\n // against the vocabulary and requirements the merge leaves\n // (`core/usecases/grade/feature.ts`). Before this, a hop nothing backs and a\n // `#req-` tag nothing resolves archived clean and turned the fleet red.\n issues.push(...flows.issues);\n\n // The `Realizes:` join, both directions, over documents this walk has already\n // parsed. Skipped entirely when the feature carries no capability delta,\n // which is every feature in a fleet that has not adopted the business axis —\n // and the removal half asks for the living corpus only when something is\n // actually retired, so even an adopting fleet pays the fleet-wide read on the\n // features that earn it.\n if (capabilityDeltas.length > 0) {\n // The three arms travel as three, and flattening two of them to `[]` is\n // the one thing this must not do: `capability.uncovered` says in words that\n // no flow keeps the promise, so handing it an empty list for a corpus loam\n // could not read or could not resolve makes it assert a negative it never\n // evaluated — and gate on it. `claims.ts`'s header states the rule; this is\n // where a caller could break it.\n issues.push(\n ...uncoveredIssues(\n capabilityDeltas,\n serviceDeltas,\n kept.kind === \"read\"\n ? { graded: true, kept: kept.kept }\n : { graded: false, why: kept.kind === \"ungraded\" ? kept.why : kept.errors.join(\"; \") },\n ),\n );\n issues.push(\n ...(await removedRealizedIssues({\n capabilities: capabilityDeltas,\n deltas: serviceDeltas,\n living: () => livingRealizers(docsDir, context),\n })),\n );\n }\n\n // The living flows whose `#req-` tag claims a promise this feature rewrites or\n // retires (`core/usecases/delta/modified.ts`). Asked only when a requirement is\n // MODIFIED or REMOVED, on either corpus, so no other feature reads a flow for it.\n if ([...capabilityDeltas, ...servicePairs].some((doc) => doc.reqs.some((r) => r.kind === \"MODIFIED\" || r.kind === \"REMOVED\"))) {\n const ctx = context ?? new FleetContext();\n issues.push(\n ...(await changedPromiseFlowIssues({\n docsDir,\n capabilities: capabilityDeltas,\n services: servicePairs,\n vocabulary: await ctx.capabilities(docsDir),\n known: new Set(await enumeratedServiceIds(docsDir, ctx)),\n serviceModel: (paths) => ctx.serviceModel(docsDir, paths),\n architecture: () => ctx.architecture(docsDir),\n readRequirements: (path) => ctx.readRequirements(path),\n })),\n );\n }\n\n return issues;\n}\n\n/**\n * Every LIVING service requirements document in the fleet, for the one question\n * that has to ask about services this feature never mentions: does anything out\n * there still realize a capability requirement this feature retires?\n *\n * The same shape and the same cost `openapi.remove-op-consumed` pays through\n * the consumer index (`core/consumers/`) one axis over, and for the same reason — the merge\n * deletes the promise while the `Realizes:` line stays, so the very next\n * `validate --all` reports a breach on a repository whose author was never in\n * this feature. Called behind a thunk (`removedRealizedIssues` invokes it only\n * when something is retired), so a feature that removes nothing never runs it.\n */\nasync function livingRealizers(docsDir: DocsDir, context?: FleetContext): Promise<RealizingDoc[]> {\n const out: RealizingDoc[] = [];\n for (const service of await enumeratedServiceIds(docsDir, context)) {\n const paths = await locateServicePaths(docsDir, service, context);\n for (const axis of SPEC_AXES) {\n const path = paths[axis.key];\n if (!existsSync(path)) continue;\n const reqs =\n context === undefined ? parseRequirements(await readFile(path, \"utf8\")) : await context.readRequirements(path);\n out.push({ service, file: axis.file, reqs });\n }\n }\n return out;\n}\n\n/**\n * One delta document against one living document: the three passes, in the\n * order that makes their messages readable.\n *\n * The order is load-bearing. `deltaDocumentIssues` settles what the delta says\n * about itself, `livingDocumentIssues` settles whether the living text can be\n * selected in at all, and only then does `selectionIssues` compare them — a pin\n * the document pass already refused must not also be reported stale, which\n * would send its author to `loam rebase` for a problem rebase does not fix.\n *\n * The parsed delta requirements come back with the issues because the capability\n * corpus grades them a second way (`capabilityDocIssues`) and re-reading the\n * file to do it would be a second parse of bytes already in hand — and, without\n * a context, a second chance for the two passes to disagree about the document.\n * The LIVING index rides back for the same reason once more: the removal\n * direction of the `Realizes:` join has to resolve a REMOVED spelled by heading\n * into the id it retires, and that id is a fact about the living document.\n */\nasync function gradeDelta(\n scope: DeltaScope,\n livingPath: string,\n reads: DeltaReads,\n): Promise<{ issues: Issue[]; reqs: Requirement[]; living: LivingIndex }> {\n const { claims, context } = reads;\n const raw = context === undefined ? await readFile(scope.specPath, \"utf8\") : await context.readText(scope.specPath);\n const reqs = context === undefined ? parseRequirements(raw) : await context.readRequirements(scope.specPath);\n const living = indexLiving(\n !existsSync(livingPath)\n ? []\n : context === undefined\n ? parseRequirements(await readFile(livingPath, \"utf8\"))\n : await context.readRequirements(livingPath),\n );\n return {\n reqs,\n living,\n issues: [\n ...deltaDocumentIssues(scope, raw, reqs),\n ...livingDocumentIssues(scope, living),\n ...(await selectionIssues(scope, reqs, living, claims)),\n ],\n };\n}\n"]}
@@ -25,8 +25,8 @@
25
25
  * caller, whether or not the edge carries an `op` (an op edge and a plain
26
26
  * edge are one claim of dependence each);
27
27
  * 2. a drawn edge carrying `consumes` — the rule of
28
- * commands/validate/service/events/events.ts: `consumes` binds the edge's
29
- * TARGET as the consumer and its SOURCE as the producer, so an edge drawn
28
+ * core/c4/direction/direction.ts: `consumes` binds the edge's TARGET as
29
+ * the consumer, so the source is what it consumes from, and an edge drawn
30
30
  * producer→consumer is fan-in on the SOURCE. Such an edge contributes
31
31
  * join 2 ONLY: on the event spine THE ARROW FOLLOWS THE MESSAGE (the
32
32
  * scaffolded landscape's own doctrine, examples/docs' landscape spells
@@ -1 +1 @@
1
- {"version":3,"file":"fanin.js","sourceRoot":"","sources":["../../../src/core/dependencies/fanin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA2CH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,UAAU,CAAC,GAAiB;IAC1C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IACjD,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAE,QAAgB,EAAQ,EAAE;QAC1D,IAAI,QAAQ,KAAK,QAAQ;YAAE,OAAO;QAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QACzD,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QACzD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClB,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,qEAAqE;YACrE,0DAA0D;YAC1D,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;gBAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;gBAChD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC9C,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC7B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACjC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC7B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrC,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE;gBAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC","sourcesContent":["/**\n * Fleet fan-in: for each service, how many DISTINCT other known services\n * depend on it — the blast radius behind `loam list --needs-work\n * --review-order`. Reviewing the most-depended-on service first is the one\n * ordering a campaign lead cannot derive by eye from the worklist.\n *\n * Pure derivation, derived-never-stored, in the package that already owns\n * who-depends-on-whom: `core/dependencies/` derives the feature ordering from\n * artifact identities, and this module derives the service-level analog. The\n * command layer (commands/list/review.ts) gathers the inputs from reads the\n * repository already pays for and hands them in as data, so the module itself\n * imports nothing — no IO, no parser import, never prints. The edge slice is\n * structural for the same reason `core/vocabulary/maturity.ts`'s EdgeSource\n * is: `core/c4/likec4.ts`'s `Rel` satisfies it without an import edge into\n * the parser.\n */\n\n/** The slice of a drawn relationship the joins read. */\nexport interface FanInEdge {\n source: string;\n target: string;\n /** AsyncAPI message this edge CONSUMES — binds the TARGET as the consumer. */\n consumes?: string;\n}\n\n/** One service's contract slice: what it declares it sends, and what its living requirements consume. */\nexport interface FanInContracts {\n /** Message names this service's asyncapi.yaml reaches from an `action: send` operation. */\n sent: readonly string[];\n /** Message names the service's living (non-REMOVED) spec/arch requirements carry on `Consumes:` lines. */\n consumed: readonly string[];\n}\n\n/**\n * The fleet map's contribution, as a variant rather than optional fields: a\n * landscape either parses — and then it has relationships and a resolver — or\n * it proves nothing, and there is no half-state in which `parses: false`\n * arrives carrying edges anyway. Positive evidence only, the rule\n * `landscapeEvidence` (core/vocabulary/maturity.ts) spells for the same map:\n * an absent or unparseable landscape contributes NOTHING to the edge joins,\n * while the requirement join below still counts.\n */\nexport type FanInLandscape =\n | {\n parses: true;\n relationships: readonly FanInEdge[];\n /** The shared container-aware resolver (`core/c4/resolve/service.ts` serviceResolver), widened to plain strings. */\n svcOf: (id: string) => string;\n }\n | { parses: false };\n\nexport interface FanInRequest {\n /** Every known service id — the universe both ends of every join are filtered to. */\n services: readonly string[];\n landscape: FanInLandscape;\n contracts: ReadonlyMap<string, FanInContracts>;\n}\n\n/**\n * Distinct known consumers per service, as a count for every id in\n * `req.services` (0 when nothing depends on it — a proven zero, since only\n * positive evidence ever increments it).\n *\n * Three joins, unioned by DISTINCT consumer so overlapping evidence for one\n * caller counts once:\n * 1. a drawn edge WITHOUT `consumes` into a service — its source is a\n * caller, whether or not the edge carries an `op` (an op edge and a plain\n * edge are one claim of dependence each);\n * 2. a drawn edge carrying `consumes` — the rule of\n * commands/validate/service/events/events.ts: `consumes` binds the edge's\n * TARGET as the consumer and its SOURCE as the producer, so an edge drawn\n * producer→consumer is fan-in on the SOURCE. Such an edge contributes\n * join 2 ONLY: on the event spine THE ARROW FOLLOWS THE MESSAGE (the\n * scaffolded landscape's own doctrine, examples/docs' landscape spells\n * it), so the arrow of a delivery edge is not a call into its target —\n * reading it as one would rank a pure event sink above a real dependency\n * and hand a bound broker service one phantom dependant per consumer;\n * 3. the landscape-independent requirement join — a service consumes a\n * message some other service declares `action: send` for.\n * Self-dependence proves nothing, and an endpoint outside the known-service\n * set (an actor, an `#external` system, an unresolved id) is not a fleet\n * caller — review order ranks work the FLEET depends on.\n */\nexport function fleetFanIn(req: FanInRequest): Map<string, number> {\n const known = new Set(req.services);\n const consumers = new Map<string, Set<string>>();\n const depend = (producer: string, consumer: string): void => {\n if (producer === consumer) return;\n if (!known.has(producer) || !known.has(consumer)) return;\n const set = consumers.get(producer) ?? new Set<string>();\n set.add(consumer);\n consumers.set(producer, set);\n };\n\n if (req.landscape.parses) {\n const { relationships, svcOf } = req.landscape;\n for (const r of relationships) {\n const source = svcOf(r.source);\n const target = svcOf(r.target);\n // The variants of the join comment above: a delivery edge (consumes)\n // claims producer→consumer, never a call into its target.\n if (r.consumes === undefined) depend(target, source);\n else depend(source, target);\n }\n }\n\n // Join 3. Producer index first: message name → every service declaring it\n // sent. A message more than one service declares lands its subscribers on\n // every declarer — the derivation never guesses which declaration wins.\n const producers = new Map<string, string[]>();\n for (const [id, slice] of req.contracts) {\n if (!known.has(id)) continue;\n for (const message of slice.sent) {\n producers.set(message, [...(producers.get(message) ?? []), id]);\n }\n }\n for (const [id, slice] of req.contracts) {\n if (!known.has(id)) continue;\n for (const message of slice.consumed) {\n for (const producer of producers.get(message) ?? []) depend(producer, id);\n }\n }\n\n return new Map(req.services.map((id) => [id, consumers.get(id)?.size ?? 0]));\n}\n"]}
1
+ {"version":3,"file":"fanin.js","sourceRoot":"","sources":["../../../src/core/dependencies/fanin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA2CH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,UAAU,CAAC,GAAiB;IAC1C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IACjD,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAE,QAAgB,EAAQ,EAAE;QAC1D,IAAI,QAAQ,KAAK,QAAQ;YAAE,OAAO;QAClC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QACzD,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QACzD,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClB,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,qEAAqE;YACrE,0DAA0D;YAC1D,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;gBAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;gBAChD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC9C,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC7B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACjC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAC7B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrC,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE;gBAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC","sourcesContent":["/**\n * Fleet fan-in: for each service, how many DISTINCT other known services\n * depend on it — the blast radius behind `loam list --needs-work\n * --review-order`. Reviewing the most-depended-on service first is the one\n * ordering a campaign lead cannot derive by eye from the worklist.\n *\n * Pure derivation, derived-never-stored, in the package that already owns\n * who-depends-on-whom: `core/dependencies/` derives the feature ordering from\n * artifact identities, and this module derives the service-level analog. The\n * command layer (commands/list/review.ts) gathers the inputs from reads the\n * repository already pays for and hands them in as data, so the module itself\n * imports nothing — no IO, no parser import, never prints. The edge slice is\n * structural for the same reason `core/vocabulary/maturity.ts`'s EdgeSource\n * is: `core/c4/likec4.ts`'s `Rel` satisfies it without an import edge into\n * the parser.\n */\n\n/** The slice of a drawn relationship the joins read. */\nexport interface FanInEdge {\n source: string;\n target: string;\n /** AsyncAPI message this edge CONSUMES — binds the TARGET as the consumer. */\n consumes?: string;\n}\n\n/** One service's contract slice: what it declares it sends, and what its living requirements consume. */\nexport interface FanInContracts {\n /** Message names this service's asyncapi.yaml reaches from an `action: send` operation. */\n sent: readonly string[];\n /** Message names the service's living (non-REMOVED) spec/arch requirements carry on `Consumes:` lines. */\n consumed: readonly string[];\n}\n\n/**\n * The fleet map's contribution, as a variant rather than optional fields: a\n * landscape either parses — and then it has relationships and a resolver — or\n * it proves nothing, and there is no half-state in which `parses: false`\n * arrives carrying edges anyway. Positive evidence only, the rule\n * `landscapeEvidence` (core/vocabulary/maturity.ts) spells for the same map:\n * an absent or unparseable landscape contributes NOTHING to the edge joins,\n * while the requirement join below still counts.\n */\nexport type FanInLandscape =\n | {\n parses: true;\n relationships: readonly FanInEdge[];\n /** The shared container-aware resolver (`core/c4/resolve/service.ts` serviceResolver), widened to plain strings. */\n svcOf: (id: string) => string;\n }\n | { parses: false };\n\nexport interface FanInRequest {\n /** Every known service id — the universe both ends of every join are filtered to. */\n services: readonly string[];\n landscape: FanInLandscape;\n contracts: ReadonlyMap<string, FanInContracts>;\n}\n\n/**\n * Distinct known consumers per service, as a count for every id in\n * `req.services` (0 when nothing depends on it — a proven zero, since only\n * positive evidence ever increments it).\n *\n * Three joins, unioned by DISTINCT consumer so overlapping evidence for one\n * caller counts once:\n * 1. a drawn edge WITHOUT `consumes` into a service — its source is a\n * caller, whether or not the edge carries an `op` (an op edge and a plain\n * edge are one claim of dependence each);\n * 2. a drawn edge carrying `consumes` — the rule of\n * core/c4/direction/direction.ts: `consumes` binds the edge's TARGET as\n * the consumer, so the source is what it consumes from, and an edge drawn\n * producer→consumer is fan-in on the SOURCE. Such an edge contributes\n * join 2 ONLY: on the event spine THE ARROW FOLLOWS THE MESSAGE (the\n * scaffolded landscape's own doctrine, examples/docs' landscape spells\n * it), so the arrow of a delivery edge is not a call into its target —\n * reading it as one would rank a pure event sink above a real dependency\n * and hand a bound broker service one phantom dependant per consumer;\n * 3. the landscape-independent requirement join — a service consumes a\n * message some other service declares `action: send` for.\n * Self-dependence proves nothing, and an endpoint outside the known-service\n * set (an actor, an `#external` system, an unresolved id) is not a fleet\n * caller — review order ranks work the FLEET depends on.\n */\nexport function fleetFanIn(req: FanInRequest): Map<string, number> {\n const known = new Set(req.services);\n const consumers = new Map<string, Set<string>>();\n const depend = (producer: string, consumer: string): void => {\n if (producer === consumer) return;\n if (!known.has(producer) || !known.has(consumer)) return;\n const set = consumers.get(producer) ?? new Set<string>();\n set.add(consumer);\n consumers.set(producer, set);\n };\n\n if (req.landscape.parses) {\n const { relationships, svcOf } = req.landscape;\n for (const r of relationships) {\n const source = svcOf(r.source);\n const target = svcOf(r.target);\n // The variants of the join comment above: a delivery edge (consumes)\n // claims producer→consumer, never a call into its target.\n if (r.consumes === undefined) depend(target, source);\n else depend(source, target);\n }\n }\n\n // Join 3. Producer index first: message name → every service declaring it\n // sent. A message more than one service declares lands its subscribers on\n // every declarer — the derivation never guesses which declaration wins.\n const producers = new Map<string, string[]>();\n for (const [id, slice] of req.contracts) {\n if (!known.has(id)) continue;\n for (const message of slice.sent) {\n producers.set(message, [...(producers.get(message) ?? []), id]);\n }\n }\n for (const [id, slice] of req.contracts) {\n if (!known.has(id)) continue;\n for (const message of slice.consumed) {\n for (const producer of producers.get(message) ?? []) depend(producer, id);\n }\n }\n\n return new Map(req.services.map((id) => [id, consumers.get(id)?.size ?? 0]));\n}\n"]}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * A feature's own topology: `features/<FEAT>/deployment/<name>.likec4`.
3
+ *
4
+ * Until this slot existed a change that stood up a standby cluster had to be
5
+ * written in two places at once — the requirement in the feature, the topology
6
+ * edited straight into `architecture/` — because `delta.likec4` refuses a
7
+ * `deployment { }` block and always will: that document re-declares the
8
+ * landscape's own identifiers and carries its own `specification`, so it cannot
9
+ * be staged beside the map in one LikeC4 project. A document that declares
10
+ * NEITHER can, which is exactly why the use-case axis put its flows in files of
11
+ * their own, and why this mirrors it rather than inventing a shape.
12
+ *
13
+ * WHAT MAKES IT WORK, and it was measured before the axis was written: LikeC4
14
+ * `extend` resolves across documents of one project. A feature writes
15
+ *
16
+ * deployment {
17
+ * extend eu {
18
+ * dcC = datacenter 'DC-C' { k8sC = cluster 'cluster-c' { … } }
19
+ * }
20
+ * eu.dcA.k8sA -> eu.dcC.k8sC 'async replication'
21
+ * }
22
+ *
23
+ * and the living region gains a datacenter with the living file untouched. So
24
+ * the merge is a copy, not a splice, and `unarchive` needs nothing new — a
25
+ * create is undone by deleting, which the snapshot manifest already records for
26
+ * every write in the plan.
27
+ *
28
+ * CREATE-ONLY, for `core/usecases/delta/flows.ts`'s reasons rather than a
29
+ * weaker version of them. A requirement delta merges INTO a living document and
30
+ * two features editing one requirement would overwrite each other silently; a
31
+ * topology document has no such algebra — the file is one block, the merge is a
32
+ * whole-file copy, and there is nothing to merge partially. Rewriting a living
33
+ * one through a feature would be a silent whole-file replacement with no pin to
34
+ * collide on, where editing `architecture/<name>.likec4` directly in the same
35
+ * pull request produces an ordinary git conflict a human resolves.
36
+ *
37
+ * ## Graded against the map its own merge would leave behind
38
+ *
39
+ * `ARCH-LOAM-FEATURE-CORPUS`, and this axis needs it more literally than the
40
+ * use-case one does: a feature that stands a service up in a new cluster
41
+ * declares that service in its `delta.likec4` and instances it here, so the
42
+ * element the topology names exists ONLY post-merge. Read against the living
43
+ * landscape it is unresolved, LikeC4 refuses the whole project, and loam would
44
+ * report a hole against a file whose only fault is arriving with the change
45
+ * that makes it true.
46
+ *
47
+ * The staging is `core/c4/project/staged.ts`, shared with the use-case slot
48
+ * rather than copied — the two would have disagreed first about the merge
49
+ * preview, which is the one thing both refusals rest on.
50
+ */
51
+ import { existsSync } from "node:fs";
52
+ import { basename, join } from "node:path";
53
+ import { architectureDir } from "../c4/project/architecture.js";
54
+ import { stageMergedProject } from "../c4/project/staged.js";
55
+ import {} from "../c4/likec4.js";
56
+ import { relativeDocuments } from "../c4/project/documents.js";
57
+ import { featureDeploymentDir } from "../repo/authored/paths.js";
58
+ import { reservedArchitectureName } from "../repo/paths.js";
59
+ import {} from "../vocabulary/issue.js";
60
+ /**
61
+ * The topology documents one feature carries, ordered by path.
62
+ *
63
+ * Zero readdirs when the feature has no `deployment/` directory, which is every
64
+ * feature in a fleet that has not adopted the axis: `architectureDocuments`
65
+ * answers an absent directory with an empty list rather than a throw. That
66
+ * directory's existence is the whole per-feature opt-in.
67
+ */
68
+ export async function featureDeployments(featureDir) {
69
+ return relativeDocuments(featureDeploymentDir(featureDir));
70
+ }
71
+ /**
72
+ * The LIVING path a feature-local topology document addresses.
73
+ *
74
+ * Straight into `architecture/`, with no subdirectory of its own — unlike
75
+ * `architecture/usecases/`, and the difference is the model rather than taste.
76
+ * A `deployment { }` block is part of the fleet's ONE architecture project and
77
+ * is read as such by every loader; filing it under a subdirectory would suggest
78
+ * a second project, which is exactly the reading `subsystems.likec4` and
79
+ * `landscape.likec4` already refuse by sitting at the top level together.
80
+ *
81
+ * `rel` is joined un-split, which `node:path` normalizes. What holds the join is
82
+ * PROVENANCE: every `rel` here came out of an `architectureDocuments` walk, so
83
+ * each component is a directory entry that exists. No caller takes one from argv.
84
+ */
85
+ export function livingDeploymentPath(docsDir, rel) {
86
+ return join(architectureDir(docsDir), rel);
87
+ }
88
+ /**
89
+ * The two things that can go wrong about a topology document's NAME, before
90
+ * anything about its contents is asked.
91
+ *
92
+ * `deployment.doc-reserved` — the name is one loam owns inside `architecture/`.
93
+ * The merge writes straight into that directory, unlike a flow, which lands
94
+ * under `usecases/`, so `<name>` is free to collide with the living map, either
95
+ * of the two GENERATED views files (`subsystems.likec4` and `services.likec4`),
96
+ * or the flow tree. `reservedArchitectureName` carries which and why. It is
97
+ * asked FIRST and WITHOUT an existence test, because three of the four fail
98
+ * silently when the target does not exist yet.
99
+ *
100
+ * `deployment.doc-exists` — a topology document this feature introduces that
101
+ * the living `architecture/` already holds.
102
+ *
103
+ * An ERROR and never a warning-that-gates, for `usecase.flow-exists`'s reason:
104
+ * there is no legal reading of it. The merge would replace an authored topology
105
+ * wholesale, and no `--approve` makes that a different act than it is. Two
106
+ * features introducing the same file is the case that makes it load-bearing
107
+ * rather than pedantic — whichever archives first creates the living document,
108
+ * and the second is told before it merges that its own file is the one to
109
+ * delete.
110
+ *
111
+ * The collision is on the FILE, not on what is inside it. Two features may both
112
+ * extend the same living region from two documents of their own and both
113
+ * archive; that is the shape `extend` exists for, and refusing it would make the
114
+ * slot useless for the fleet-wide change it was built for.
115
+ */
116
+ export async function deploymentDeltaIssues(docsDir, featureDir) {
117
+ const docs = await featureDeployments(featureDir);
118
+ const issues = [];
119
+ // The reservation runs FIRST and independently of existence, because three of
120
+ // the four names it refuses need not exist for the merge to be wrong. A
121
+ // topology copied over either GENERATED file — `subsystems.likec4` or
122
+ // `services.likec4` — is destroyed by the next `loam subsystem sync` and is
123
+ // invisible to every project load until then: the merge would report
124
+ // `created architecture/subsystems.likec4` and nothing downstream would ever
125
+ // say another word about it.
126
+ for (const doc of docs) {
127
+ const reason = reservedArchitectureName(doc.rel);
128
+ if (reason === null)
129
+ continue;
130
+ issues.push({
131
+ severity: "error",
132
+ code: "deployment.doc-reserved",
133
+ subject: doc.rel,
134
+ message: `features/<FEAT>/deployment/${doc.rel} would merge to architecture/${doc.rel}, and that name is loam's: ` +
135
+ `${reason}. Rename the feature's document — its name is yours to choose, and \`extend\` is how it adds to ` +
136
+ "what a living document already declares.",
137
+ });
138
+ }
139
+ return issues.concat(docs
140
+ .filter((doc) => reservedArchitectureName(doc.rel) === null)
141
+ .filter((doc) => existsSync(livingDeploymentPath(docsDir, doc.rel)))
142
+ .map((doc) => ({
143
+ severity: "error",
144
+ code: "deployment.doc-exists",
145
+ subject: doc.rel,
146
+ message: `architecture/${doc.rel} already exists, so this feature's topology would replace an authored one wholesale — ` +
147
+ "a feature-local deployment document INTRODUCES topology, it does not rewrite it. " +
148
+ `Delete features/<FEAT>/deployment/${doc.rel} and edit architecture/${doc.rel} directly in this same change, ` +
149
+ "where git produces an ordinary conflict if somebody else is editing it too. " +
150
+ `To ADD to what that file declares, write a document of another name that says \`extend\` — ` +
151
+ `\`${suggest(doc.rel)}\` — which is what lets two features grow one region without touching each other.`,
152
+ })));
153
+ }
154
+ /**
155
+ * Parse this feature's topology documents over the post-merge map.
156
+ *
157
+ * A feature with no `deployment/` directory is `read` at the cost of one walk
158
+ * over a directory that is not there — the axis's per-feature opt-in, and the
159
+ * whole price a fleet that has not adopted it pays. Nothing is staged, nothing
160
+ * is parsed, and no temp tree is made.
161
+ *
162
+ * The verdict is deliberately BINARY. The use-case overlay hands its caller the
163
+ * views it read because the axis has questions about their content; this one
164
+ * has none — a topology document is checked by being parseable against the map
165
+ * the merge will write, and everything else about it is graded by
166
+ * `loam validate --all` once it is living, exactly as the landscape is.
167
+ */
168
+ export async function readFeatureDeployments(req) {
169
+ const docs = await featureDeployments(req.featureDir);
170
+ if (docs.length === 0)
171
+ return { kind: "read" };
172
+ // AFTER the early return: the fleet read is paid only by a feature that
173
+ // actually brings a topology document.
174
+ const models = req.models === undefined ? undefined : await req.models();
175
+ const staged = await stageMergedProject({
176
+ docsDir: req.docsDir,
177
+ featureDir: req.featureDir,
178
+ featureId: req.featureId,
179
+ documents: docs.map((doc) => ({ rel: doc.rel, path: doc.path })),
180
+ ...(req.load === undefined ? {} : { load: req.load }),
181
+ ...(models === undefined ? {} : { models }),
182
+ });
183
+ return staged.kind === "unreadable" ? { kind: "unreadable", errors: staged.errors } : { kind: "read" };
184
+ }
185
+ /** A free name near the colliding one, so the fix is a rename the author can copy. */
186
+ function suggest(rel) {
187
+ const name = basename(rel).replace(/\.likec4$/, "");
188
+ const dir = rel.slice(0, rel.length - basename(rel).length);
189
+ return `${dir}${name}-extend.likec4`;
190
+ }
191
+ //# sourceMappingURL=delta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta.js","sourceRoot":"","sources":["../../../src/core/deployment/delta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A feature's own topology: `features/<FEAT>/deployment/<name>.likec4`.\n *\n * Until this slot existed a change that stood up a standby cluster had to be\n * written in two places at once — the requirement in the feature, the topology\n * edited straight into `architecture/` — because `delta.likec4` refuses a\n * `deployment { }` block and always will: that document re-declares the\n * landscape's own identifiers and carries its own `specification`, so it cannot\n * be staged beside the map in one LikeC4 project. A document that declares\n * NEITHER can, which is exactly why the use-case axis put its flows in files of\n * their own, and why this mirrors it rather than inventing a shape.\n *\n * WHAT MAKES IT WORK, and it was measured before the axis was written: LikeC4\n * `extend` resolves across documents of one project. A feature writes\n *\n * deployment {\n * extend eu {\n * dcC = datacenter 'DC-C' { k8sC = cluster 'cluster-c' { … } }\n * }\n * eu.dcA.k8sA -> eu.dcC.k8sC 'async replication'\n * }\n *\n * and the living region gains a datacenter with the living file untouched. So\n * the merge is a copy, not a splice, and `unarchive` needs nothing new — a\n * create is undone by deleting, which the snapshot manifest already records for\n * every write in the plan.\n *\n * CREATE-ONLY, for `core/usecases/delta/flows.ts`'s reasons rather than a\n * weaker version of them. A requirement delta merges INTO a living document and\n * two features editing one requirement would overwrite each other silently; a\n * topology document has no such algebra — the file is one block, the merge is a\n * whole-file copy, and there is nothing to merge partially. Rewriting a living\n * one through a feature would be a silent whole-file replacement with no pin to\n * collide on, where editing `architecture/<name>.likec4` directly in the same\n * pull request produces an ordinary git conflict a human resolves.\n *\n * ## Graded against the map its own merge would leave behind\n *\n * `ARCH-LOAM-FEATURE-CORPUS`, and this axis needs it more literally than the\n * use-case one does: a feature that stands a service up in a new cluster\n * declares that service in its `delta.likec4` and instances it here, so the\n * element the topology names exists ONLY post-merge. Read against the living\n * landscape it is unresolved, LikeC4 refuses the whole project, and loam would\n * report a hole against a file whose only fault is arriving with the change\n * that makes it true.\n *\n * The staging is `core/c4/project/staged.ts`, shared with the use-case slot\n * rather than copied — the two would have disagreed first about the merge\n * preview, which is the one thing both refusals rest on.\n */\nimport { existsSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { architectureDir } from \"../c4/project/architecture.js\";\nimport { stageMergedProject } from \"../c4/project/staged.js\";\nimport { type LoadedDoc } from \"../c4/likec4.js\";\nimport type { ExtendingModel } from \"../c4/splice/contract.js\";\nimport { relativeDocuments } from \"../c4/project/documents.js\";\nimport { featureDeploymentDir } from \"../repo/authored/paths.js\";\nimport { reservedArchitectureName } from \"../repo/paths.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\nimport { type Issue } from \"../vocabulary/issue.js\";\n\n/** One topology document a feature carries. */\nexport interface FeatureDeployment {\n /**\n * The path INSIDE the feature's `deployment/` directory, `/`-separated — the\n * id this axis has. Nesting is spelled by the tree, as it is for every other\n * authored tree in the repo.\n */\n rel: string;\n /** The absolute path of the authored file. */\n path: string;\n}\n\n/**\n * The topology documents one feature carries, ordered by path.\n *\n * Zero readdirs when the feature has no `deployment/` directory, which is every\n * feature in a fleet that has not adopted the axis: `architectureDocuments`\n * answers an absent directory with an empty list rather than a throw. That\n * directory's existence is the whole per-feature opt-in.\n */\nexport async function featureDeployments(featureDir: FeatureDir): Promise<FeatureDeployment[]> {\n return relativeDocuments(featureDeploymentDir(featureDir));\n}\n\n/**\n * The LIVING path a feature-local topology document addresses.\n *\n * Straight into `architecture/`, with no subdirectory of its own — unlike\n * `architecture/usecases/`, and the difference is the model rather than taste.\n * A `deployment { }` block is part of the fleet's ONE architecture project and\n * is read as such by every loader; filing it under a subdirectory would suggest\n * a second project, which is exactly the reading `subsystems.likec4` and\n * `landscape.likec4` already refuse by sitting at the top level together.\n *\n * `rel` is joined un-split, which `node:path` normalizes. What holds the join is\n * PROVENANCE: every `rel` here came out of an `architectureDocuments` walk, so\n * each component is a directory entry that exists. No caller takes one from argv.\n */\nexport function livingDeploymentPath(docsDir: DocsDir, rel: string): string {\n return join(architectureDir(docsDir), rel);\n}\n\n/**\n * The two things that can go wrong about a topology document's NAME, before\n * anything about its contents is asked.\n *\n * `deployment.doc-reserved` — the name is one loam owns inside `architecture/`.\n * The merge writes straight into that directory, unlike a flow, which lands\n * under `usecases/`, so `<name>` is free to collide with the living map, either\n * of the two GENERATED views files (`subsystems.likec4` and `services.likec4`),\n * or the flow tree. `reservedArchitectureName` carries which and why. It is\n * asked FIRST and WITHOUT an existence test, because three of the four fail\n * silently when the target does not exist yet.\n *\n * `deployment.doc-exists` — a topology document this feature introduces that\n * the living `architecture/` already holds.\n *\n * An ERROR and never a warning-that-gates, for `usecase.flow-exists`'s reason:\n * there is no legal reading of it. The merge would replace an authored topology\n * wholesale, and no `--approve` makes that a different act than it is. Two\n * features introducing the same file is the case that makes it load-bearing\n * rather than pedantic — whichever archives first creates the living document,\n * and the second is told before it merges that its own file is the one to\n * delete.\n *\n * The collision is on the FILE, not on what is inside it. Two features may both\n * extend the same living region from two documents of their own and both\n * archive; that is the shape `extend` exists for, and refusing it would make the\n * slot useless for the fleet-wide change it was built for.\n */\nexport async function deploymentDeltaIssues(docsDir: DocsDir, featureDir: FeatureDir): Promise<Issue[]> {\n const docs = await featureDeployments(featureDir);\n const issues: Issue[] = [];\n // The reservation runs FIRST and independently of existence, because three of\n // the four names it refuses need not exist for the merge to be wrong. A\n // topology copied over either GENERATED file — `subsystems.likec4` or\n // `services.likec4` — is destroyed by the next `loam subsystem sync` and is\n // invisible to every project load until then: the merge would report\n // `created architecture/subsystems.likec4` and nothing downstream would ever\n // say another word about it.\n for (const doc of docs) {\n const reason = reservedArchitectureName(doc.rel);\n if (reason === null) continue;\n issues.push({\n severity: \"error\",\n code: \"deployment.doc-reserved\",\n subject: doc.rel,\n message:\n `features/<FEAT>/deployment/${doc.rel} would merge to architecture/${doc.rel}, and that name is loam's: ` +\n `${reason}. Rename the feature's document — its name is yours to choose, and \\`extend\\` is how it adds to ` +\n \"what a living document already declares.\",\n });\n }\n return issues.concat(\n docs\n .filter((doc) => reservedArchitectureName(doc.rel) === null)\n .filter((doc) => existsSync(livingDeploymentPath(docsDir, doc.rel)))\n .map((doc) => ({\n severity: \"error\" as const,\n code: \"deployment.doc-exists\" as const,\n subject: doc.rel,\n message:\n `architecture/${doc.rel} already exists, so this feature's topology would replace an authored one wholesale — ` +\n \"a feature-local deployment document INTRODUCES topology, it does not rewrite it. \" +\n `Delete features/<FEAT>/deployment/${doc.rel} and edit architecture/${doc.rel} directly in this same change, ` +\n \"where git produces an ordinary conflict if somebody else is editing it too. \" +\n `To ADD to what that file declares, write a document of another name that says \\`extend\\` — ` +\n `\\`${suggest(doc.rel)}\\` — which is what lets two features grow one region without touching each other.`,\n })),\n );\n}\n\n/** What the caller needs to grade this feature's topology against the merge preview. */\nexport interface DeploymentOverlayRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n /** The feature's id — the tag that selects the delta's own additions. */\n featureId: string;\n /**\n * The caller's memoised LikeC4 read, when it has one. A FUNCTION rather than\n * the `FleetContext` itself, for the reason the use-case overlay states: one\n * memoised read does not justify an edge from this package to\n * `core/fleet-context.ts`, and the edge would push it up a DAG level for\n * nothing.\n */\n load?: (path: string) => Promise<LoadedDoc>;\n /**\n * The fleet's extending models, for the merge preview — the SAME list\n * `loam archive` routes with. A preview computed without them holds interior\n * the archive puts in `services/<…>/model.likec4`, so a topology document\n * naming `<service-fqn>.<container>` reads clean here and against nothing\n * once it is living; `deployment.doc-invalid` is the gate that then fails\n * open (verification 2026-09-04, review F8).\n *\n * A THUNK for `load`'s reason and one more: the early return below is the\n * axis's per-feature opt-in, and a feature with no `deployment/` directory\n * must not pay a fleet enumeration to be told so.\n */\n models?: () => Promise<readonly ExtendingModel[]>;\n}\n\n/** Whether this feature's topology could be read at all, against the map its merge would leave. */\nexport type DeploymentOverlay = { kind: \"read\" } | { kind: \"unreadable\"; errors: string[] };\n\n/**\n * Parse this feature's topology documents over the post-merge map.\n *\n * A feature with no `deployment/` directory is `read` at the cost of one walk\n * over a directory that is not there — the axis's per-feature opt-in, and the\n * whole price a fleet that has not adopted it pays. Nothing is staged, nothing\n * is parsed, and no temp tree is made.\n *\n * The verdict is deliberately BINARY. The use-case overlay hands its caller the\n * views it read because the axis has questions about their content; this one\n * has none — a topology document is checked by being parseable against the map\n * the merge will write, and everything else about it is graded by\n * `loam validate --all` once it is living, exactly as the landscape is.\n */\nexport async function readFeatureDeployments(req: DeploymentOverlayRequest): Promise<DeploymentOverlay> {\n const docs = await featureDeployments(req.featureDir);\n if (docs.length === 0) return { kind: \"read\" };\n // AFTER the early return: the fleet read is paid only by a feature that\n // actually brings a topology document.\n const models = req.models === undefined ? undefined : await req.models();\n const staged = await stageMergedProject({\n docsDir: req.docsDir,\n featureDir: req.featureDir,\n featureId: req.featureId,\n documents: docs.map((doc) => ({ rel: doc.rel, path: doc.path })),\n ...(req.load === undefined ? {} : { load: req.load }),\n ...(models === undefined ? {} : { models }),\n });\n return staged.kind === \"unreadable\" ? { kind: \"unreadable\", errors: staged.errors } : { kind: \"read\" };\n}\n\n/** A free name near the colliding one, so the fix is a rename the author can copy. */\nfunction suggest(rel: string): string {\n const name = basename(rel).replace(/\\.likec4$/, \"\");\n const dir = rel.slice(0, rel.length - basename(rel).length);\n return `${dir}${name}-extend.likec4`;\n}\n"]}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The deployment axis: which objects on the fleet's TOPOLOGY carry a rule, and
3
+ * who owns each of them.
4
+ *
5
+ * A `#obl-` tag is a claim about the object it sits on, and the object's model
6
+ * is not a property any reader can see. Before this module the same undeclared
7
+ * tag was `obligation.unknown` on a container and SILENCE on a datacenter,
8
+ * because `validate/fleet/obligations.ts` walked the logical model only — the
9
+ * fail-open case where a rule an architect placed does not exist for any check,
10
+ * and a reviewer has no way to tell that from a rule that passed. This is the
11
+ * derivation that closes it (FEAT-1's ARCH-LOAM-DEPLOY-TAGGED).
12
+ *
13
+ * ## The `where` string is the `Covers:` line
14
+ *
15
+ * Every finding about a tagged object tells its reader to write
16
+ * `Covers: <where>`, so `where` is spelled as the grammar reads it —
17
+ * `node:eu.dcA`, `node:a.db -> node:b.db` — and never as a bare id. A message
18
+ * that names an object one way and asks for a line spelled another way sends
19
+ * the reader round the loop twice.
20
+ *
21
+ * ## Who owns a datacenter
22
+ *
23
+ * Nobody, and the answer is `undefined` rather than a guess. An INSTANCE is
24
+ * owned by the service its logical element belongs to, which is a real join
25
+ * through the landscape's own binding; an edge between two instances of one
26
+ * service is owned by that service; a region, a datacenter or a cluster is
27
+ * owned by whoever runs the platform, and loam has no such concept. The finding
28
+ * changes its sentence rather than naming a service that did not ask for the
29
+ * work — "write it in payment-service's arch.spec.md" is actionable, and
30
+ * "write it in <the wrong team>'s" is worse than no attribution at all.
31
+ *
32
+ * This package is one module today and gains its second with the feature-local
33
+ * `features/<FEAT>/deployment/` slot, which is the step of the axis that needs
34
+ * `repo` and `kernel`. Nothing here reads a path.
35
+ */
36
+ import {} from "../c4/likec4.js";
37
+ import {} from "../c4/parsed/deployment.js";
38
+ import { serviceResolver } from "../c4/resolve/service.js";
39
+ /**
40
+ * Every `#obl-` tag on the deployment model, one entry per (obligation, object)
41
+ * pair — the same shape, and the same bare-prefix tolerance, the logical walk
42
+ * has: a bare `#obl-` slugs to the empty string and is KEPT, because the prefix
43
+ * is the author's opt-in however little follows it, and dropping it would leave
44
+ * an object that asked to be graded silently ungraded.
45
+ */
46
+ export function taggedDeployment(deployment, elements, known, prefix) {
47
+ const owner = instanceOwners(deployment, elements, known);
48
+ const out = [];
49
+ const push = (tags, rest) => {
50
+ for (const tag of tags.filter((t) => t.startsWith(prefix))) {
51
+ out.push({ obligation: tag.slice(prefix.length), ...rest });
52
+ }
53
+ };
54
+ // Nodes first, then instances, then edges — the order the model declares them
55
+ // in, so a fleet report reads top-down the way the document does.
56
+ for (const node of deployment.nodes)
57
+ push(node.tags, { where: `node:${node.id}`, id: node.id });
58
+ for (const instance of deployment.instances) {
59
+ const service = owner.get(instance.id);
60
+ push(instance.tags, {
61
+ where: `node:${instance.id}`,
62
+ id: instance.id,
63
+ ...(service === undefined ? {} : { service }),
64
+ });
65
+ }
66
+ for (const edge of deployment.relationships) {
67
+ // An edge is owned only when BOTH ends agree. A replication edge between
68
+ // two instances of one service belongs to that service; one crossing
69
+ // services belongs to neither, and picking the target — the rule the
70
+ // logical walk uses, where the arrow means "calls" — says nothing here,
71
+ // because a deployment edge is as often symmetric as directed.
72
+ const from = owner.get(edge.source);
73
+ const to = owner.get(edge.target);
74
+ const service = from !== undefined && from === to ? from : undefined;
75
+ push(edge.tags, {
76
+ where: `node:${edge.source} -> node:${edge.target}`,
77
+ edge,
78
+ ...(service === undefined ? {} : { service }),
79
+ });
80
+ }
81
+ return out;
82
+ }
83
+ /**
84
+ * The topology one service is part of.
85
+ *
86
+ * Built for the CONTEXT PACK, which is the surface whose reader is an agent
87
+ * about to write the implementation. Before this, that pack described a
88
+ * service's calls and contracts and said nothing whatever about where it runs —
89
+ * so a failover was implemented against a map nobody had read, which is the
90
+ * gap FEAT-1's ARCH-LOAM-DEPLOY-UNCHECKED names alongside the sentence about
91
+ * what a green axis does not mean.
92
+ *
93
+ * BOTH ENDS, not only the outbound one. A replication edge is as often read
94
+ * from the standby's side as from the primary's, and a pack that showed only
95
+ * what this service points at would hide the stream it receives.
96
+ */
97
+ export function serviceTopology(deployment, elements, known, service) {
98
+ const owner = instanceOwners(deployment, elements, known);
99
+ const mine = deployment.instances.filter((i) => owner.get(i.id) === service);
100
+ const ids = new Set(mine.map((i) => i.id));
101
+ return {
102
+ instances: mine.map((i) => ({ id: i.id, element: i.element })),
103
+ edges: deployment.relationships
104
+ .filter((r) => ids.has(r.source) || ids.has(r.target))
105
+ .map((r) => ({ source: r.source, target: r.target, ...(r.title === undefined ? {} : { title: r.title }) })),
106
+ };
107
+ }
108
+ /**
109
+ * instance id → the service its logical element belongs to.
110
+ *
111
+ * Through `serviceResolver` and NOT `elementService`, and the difference is a
112
+ * defect this axis shipped once. A deployment instances a CONTAINER far more
113
+ * often than a whole system — `instanceOf orders.cache` is the ordinary way to
114
+ * say where the Redis a service owns actually runs — and `elementService` reads
115
+ * the binding or the TITLE of the element handed to it. A nested container
116
+ * carries no `metadata { service }` of its own, so that read answered "order
117
+ * cache", and `obligation.uncovered` told an author to write the requirement in
118
+ * "order cache's arch.spec.md" — a service that has never existed. The
119
+ * resolver's whole job is the ancestor walk that finds the bound system above
120
+ * it, which is the same join every other check makes about a container.
121
+ *
122
+ * `known` rides in for the reason `coversEdge` takes it: without the enumerated
123
+ * fleet the resolver's second tier — the nearest ancestor whose title names a
124
+ * real `services/<id>/` — cannot run, and a fleet that binds by title rather
125
+ * than by `metadata { service }` resolves to the title alone. It must be the
126
+ * SAME set the caller's own findings resolve with, or the message names one
127
+ * service and the check counts another.
128
+ *
129
+ * An instance of an element the landscape does not declare is left UNOWNED
130
+ * rather than filed under a fabricated name. LikeC4 refuses an unresolved
131
+ * `instanceOf` outright, so that state is reachable only when the caller hands
132
+ * in elements from a narrower document than the one the topology was parsed
133
+ * with.
134
+ */
135
+ function instanceOwners(deployment, elements, known) {
136
+ const ids = new Set(elements.map((e) => e.id));
137
+ const resolve = serviceResolver([...elements], known);
138
+ const out = new Map();
139
+ for (const instance of deployment.instances) {
140
+ if (ids.has(instance.element))
141
+ out.set(instance.id, resolve(instance.element));
142
+ }
143
+ return out;
144
+ }
145
+ //# sourceMappingURL=objects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objects.js","sourceRoot":"","sources":["../../../src/core/deployment/objects.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The deployment axis: which objects on the fleet's TOPOLOGY carry a rule, and\n * who owns each of them.\n *\n * A `#obl-` tag is a claim about the object it sits on, and the object's model\n * is not a property any reader can see. Before this module the same undeclared\n * tag was `obligation.unknown` on a container and SILENCE on a datacenter,\n * because `validate/fleet/obligations.ts` walked the logical model only — the\n * fail-open case where a rule an architect placed does not exist for any check,\n * and a reviewer has no way to tell that from a rule that passed. This is the\n * derivation that closes it (FEAT-1's ARCH-LOAM-DEPLOY-TAGGED).\n *\n * ## The `where` string is the `Covers:` line\n *\n * Every finding about a tagged object tells its reader to write\n * `Covers: <where>`, so `where` is spelled as the grammar reads it —\n * `node:eu.dcA`, `node:a.db -> node:b.db` — and never as a bare id. A message\n * that names an object one way and asks for a line spelled another way sends\n * the reader round the loop twice.\n *\n * ## Who owns a datacenter\n *\n * Nobody, and the answer is `undefined` rather than a guess. An INSTANCE is\n * owned by the service its logical element belongs to, which is a real join\n * through the landscape's own binding; an edge between two instances of one\n * service is owned by that service; a region, a datacenter or a cluster is\n * owned by whoever runs the platform, and loam has no such concept. The finding\n * changes its sentence rather than naming a service that did not ask for the\n * work — \"write it in payment-service's arch.spec.md\" is actionable, and\n * \"write it in <the wrong team>'s\" is worse than no attribution at all.\n *\n * This package is one module today and gains its second with the feature-local\n * `features/<FEAT>/deployment/` slot, which is the step of the axis that needs\n * `repo` and `kernel`. Nothing here reads a path.\n */\nimport { type Elem } from \"../c4/likec4.js\";\nimport { type DeploymentModel, type DeployRel } from \"../c4/parsed/deployment.js\";\nimport { serviceResolver } from \"../c4/resolve/service.js\";\n\n/** One deployment object carrying one obligation tag. */\nexport interface TaggedDeployment {\n /** The obligation id — the tag suffix, exactly as the logical walk reads it. */\n obligation: string;\n /**\n * How a `Covers:` line names this object, and how every message spells it:\n * `node:<id>` for a node or an instance, `node:<a> -> node:<b>` for an edge.\n */\n where: string;\n /** The service that owns it, where the topology says. Absent for a node nobody's service runs. */\n service?: string;\n /** The edge, when this object is one — the caller matches `Covers:` against it. */\n edge?: DeployRel;\n /** The node or instance id, when this object is one. */\n id?: string;\n}\n\n/**\n * Every `#obl-` tag on the deployment model, one entry per (obligation, object)\n * pair — the same shape, and the same bare-prefix tolerance, the logical walk\n * has: a bare `#obl-` slugs to the empty string and is KEPT, because the prefix\n * is the author's opt-in however little follows it, and dropping it would leave\n * an object that asked to be graded silently ungraded.\n */\nexport function taggedDeployment(\n deployment: DeploymentModel,\n elements: readonly Elem[],\n known: ReadonlySet<string>,\n prefix: string,\n): TaggedDeployment[] {\n const owner = instanceOwners(deployment, elements, known);\n const out: TaggedDeployment[] = [];\n const push = (tags: readonly string[], rest: Omit<TaggedDeployment, \"obligation\">): void => {\n for (const tag of tags.filter((t) => t.startsWith(prefix))) {\n out.push({ obligation: tag.slice(prefix.length), ...rest });\n }\n };\n\n // Nodes first, then instances, then edges — the order the model declares them\n // in, so a fleet report reads top-down the way the document does.\n for (const node of deployment.nodes) push(node.tags, { where: `node:${node.id}`, id: node.id });\n for (const instance of deployment.instances) {\n const service = owner.get(instance.id);\n push(instance.tags, {\n where: `node:${instance.id}`,\n id: instance.id,\n ...(service === undefined ? {} : { service }),\n });\n }\n for (const edge of deployment.relationships) {\n // An edge is owned only when BOTH ends agree. A replication edge between\n // two instances of one service belongs to that service; one crossing\n // services belongs to neither, and picking the target — the rule the\n // logical walk uses, where the arrow means \"calls\" — says nothing here,\n // because a deployment edge is as often symmetric as directed.\n const from = owner.get(edge.source);\n const to = owner.get(edge.target);\n const service = from !== undefined && from === to ? from : undefined;\n push(edge.tags, {\n where: `node:${edge.source} -> node:${edge.target}`,\n edge,\n ...(service === undefined ? {} : { service }),\n });\n }\n return out;\n}\n\n/** Where one service runs, and what its instances talk to across the topology. */\nexport interface ServiceTopology {\n /** This service's instances: the deployment id, and the element each one deploys. */\n instances: Array<{ id: string; element: string }>;\n /** Deployment edges with at least one end among them — replication, failover, a network hop. */\n edges: Array<{ source: string; target: string; title?: string }>;\n}\n\n/**\n * The topology one service is part of.\n *\n * Built for the CONTEXT PACK, which is the surface whose reader is an agent\n * about to write the implementation. Before this, that pack described a\n * service's calls and contracts and said nothing whatever about where it runs —\n * so a failover was implemented against a map nobody had read, which is the\n * gap FEAT-1's ARCH-LOAM-DEPLOY-UNCHECKED names alongside the sentence about\n * what a green axis does not mean.\n *\n * BOTH ENDS, not only the outbound one. A replication edge is as often read\n * from the standby's side as from the primary's, and a pack that showed only\n * what this service points at would hide the stream it receives.\n */\nexport function serviceTopology(\n deployment: DeploymentModel,\n elements: readonly Elem[],\n known: ReadonlySet<string>,\n service: string,\n): ServiceTopology {\n const owner = instanceOwners(deployment, elements, known);\n const mine = deployment.instances.filter((i) => owner.get(i.id) === service);\n const ids = new Set(mine.map((i) => i.id));\n return {\n instances: mine.map((i) => ({ id: i.id, element: i.element })),\n edges: deployment.relationships\n .filter((r) => ids.has(r.source) || ids.has(r.target))\n .map((r) => ({ source: r.source, target: r.target, ...(r.title === undefined ? {} : { title: r.title }) })),\n };\n}\n\n/**\n * instance id → the service its logical element belongs to.\n *\n * Through `serviceResolver` and NOT `elementService`, and the difference is a\n * defect this axis shipped once. A deployment instances a CONTAINER far more\n * often than a whole system — `instanceOf orders.cache` is the ordinary way to\n * say where the Redis a service owns actually runs — and `elementService` reads\n * the binding or the TITLE of the element handed to it. A nested container\n * carries no `metadata { service }` of its own, so that read answered \"order\n * cache\", and `obligation.uncovered` told an author to write the requirement in\n * \"order cache's arch.spec.md\" — a service that has never existed. The\n * resolver's whole job is the ancestor walk that finds the bound system above\n * it, which is the same join every other check makes about a container.\n *\n * `known` rides in for the reason `coversEdge` takes it: without the enumerated\n * fleet the resolver's second tier — the nearest ancestor whose title names a\n * real `services/<id>/` — cannot run, and a fleet that binds by title rather\n * than by `metadata { service }` resolves to the title alone. It must be the\n * SAME set the caller's own findings resolve with, or the message names one\n * service and the check counts another.\n *\n * An instance of an element the landscape does not declare is left UNOWNED\n * rather than filed under a fabricated name. LikeC4 refuses an unresolved\n * `instanceOf` outright, so that state is reachable only when the caller hands\n * in elements from a narrower document than the one the topology was parsed\n * with.\n */\nfunction instanceOwners(\n deployment: DeploymentModel,\n elements: readonly Elem[],\n known: ReadonlySet<string>,\n): Map<string, string> {\n const ids = new Set(elements.map((e) => e.id));\n const resolve = serviceResolver([...elements], known);\n const out = new Map<string, string>();\n for (const instance of deployment.instances) {\n if (ids.has(instance.element)) out.set(instance.id, resolve(instance.element));\n }\n return out;\n}\n"]}