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

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 (1026) hide show
  1. package/CHANGELOG.md +2548 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +494 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +317 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +161 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +251 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +194 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +236 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +250 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +219 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +334 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +47 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +282 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +88 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  398. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  399. package/dist/core/c4/parsed/specification.js +78 -0
  400. package/dist/core/c4/parsed/specification.js.map +1 -0
  401. package/dist/core/c4/parsed/values.js +57 -0
  402. package/dist/core/c4/parsed/values.js.map +1 -0
  403. package/dist/core/c4/parsed/view-ids.js +75 -0
  404. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  405. package/dist/core/c4/project/architecture.js +45 -0
  406. package/dist/core/c4/project/architecture.js.map +1 -0
  407. package/dist/core/c4/project/documents.js +58 -0
  408. package/dist/core/c4/project/documents.js.map +1 -0
  409. package/dist/core/c4/project/load.js +183 -0
  410. package/dist/core/c4/project/load.js.map +1 -0
  411. package/dist/core/c4/resolve/service.js +113 -0
  412. package/dist/core/c4/resolve/service.js.map +1 -0
  413. package/dist/core/c4/seed/fleet-file.js +201 -0
  414. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  415. package/dist/core/c4/seed/items.js +91 -0
  416. package/dist/core/c4/seed/items.js.map +1 -0
  417. package/dist/core/c4/seed/stamp.js +48 -0
  418. package/dist/core/c4/seed/stamp.js.map +1 -0
  419. package/dist/core/c4/seed/template.js +109 -0
  420. package/dist/core/c4/seed/template.js.map +1 -0
  421. package/dist/core/c4/source-mask.js +82 -0
  422. package/dist/core/c4/source-mask.js.map +1 -0
  423. package/dist/core/c4/source-scan.js +227 -0
  424. package/dist/core/c4/source-scan.js.map +1 -0
  425. package/dist/core/c4/splice/authored-source.js +101 -0
  426. package/dist/core/c4/splice/authored-source.js.map +1 -0
  427. package/dist/core/c4/splice/contract.js +10 -0
  428. package/dist/core/c4/splice/contract.js.map +1 -0
  429. package/dist/core/c4/splice/delta-blocks.js +126 -0
  430. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  431. package/dist/core/c4/splice/identity/edges.js +36 -0
  432. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  433. package/dist/core/c4/splice/landscape-merge.js +269 -0
  434. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  435. package/dist/core/c4/splice/placement.js +194 -0
  436. package/dist/core/c4/splice/placement.js.map +1 -0
  437. package/dist/core/c4/workspace.js +172 -0
  438. package/dist/core/c4/workspace.js.map +1 -0
  439. package/dist/core/capabilities/authoring/notes.js +141 -0
  440. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  441. package/dist/core/capabilities/capabilities.js +124 -0
  442. package/dist/core/capabilities/capabilities.js.map +1 -0
  443. package/dist/core/capabilities/delta/doc.js +50 -0
  444. package/dist/core/capabilities/delta/doc.js.map +1 -0
  445. package/dist/core/capabilities/delta/overlay.js +129 -0
  446. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  447. package/dist/core/capabilities/delta/summary.js +57 -0
  448. package/dist/core/capabilities/delta/summary.js.map +1 -0
  449. package/dist/core/capabilities/delta/tree.js +43 -0
  450. package/dist/core/capabilities/delta/tree.js.map +1 -0
  451. package/dist/core/capabilities/delta/uncovered.js +222 -0
  452. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  453. package/dist/core/capabilities/findings.js +342 -0
  454. package/dist/core/capabilities/findings.js.map +1 -0
  455. package/dist/core/capabilities/realizes/findings.js +181 -0
  456. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  457. package/dist/core/capabilities/realizes/join.js +143 -0
  458. package/dist/core/capabilities/realizes/join.js.map +1 -0
  459. package/dist/core/capabilities/rollup.js +199 -0
  460. package/dist/core/capabilities/rollup.js.map +1 -0
  461. package/dist/core/capabilities/tree.js +126 -0
  462. package/dist/core/capabilities/tree.js.map +1 -0
  463. package/dist/core/capabilities/usecase-join.js +277 -0
  464. package/dist/core/capabilities/usecase-join.js.map +1 -0
  465. package/dist/core/coherence/authoring/scaffold.js +177 -0
  466. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  467. package/dist/core/coherence/authoring/sentinels.js +74 -0
  468. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  469. package/dist/core/coherence/coherence.js +251 -0
  470. package/dist/core/coherence/coherence.js.map +1 -0
  471. package/dist/core/coherence/declared.js +269 -0
  472. package/dist/core/coherence/declared.js.map +1 -0
  473. package/dist/core/coherence/events/declared.js +206 -0
  474. package/dist/core/coherence/events/declared.js.map +1 -0
  475. package/dist/core/coherence/events/events.js +204 -0
  476. package/dist/core/coherence/events/events.js.map +1 -0
  477. package/dist/core/coherence/events/grades.js +166 -0
  478. package/dist/core/coherence/events/grades.js.map +1 -0
  479. package/dist/core/coherence/events/lookups.js +122 -0
  480. package/dist/core/coherence/events/lookups.js.map +1 -0
  481. package/dist/core/coherence/events/merged.js +40 -0
  482. package/dist/core/coherence/events/merged.js.map +1 -0
  483. package/dist/core/coherence/living.js +198 -0
  484. package/dist/core/coherence/living.js.map +1 -0
  485. package/dist/core/coherence/lookups.js +155 -0
  486. package/dist/core/coherence/lookups.js.map +1 -0
  487. package/dist/core/coherence/pending.js +39 -0
  488. package/dist/core/coherence/pending.js.map +1 -0
  489. package/dist/core/conflict-markers.js +60 -0
  490. package/dist/core/conflict-markers.js.map +1 -0
  491. package/dist/core/delta/claims.js +73 -0
  492. package/dist/core/delta/claims.js.map +1 -0
  493. package/dist/core/delta/delta.js +296 -0
  494. package/dist/core/delta/delta.js.map +1 -0
  495. package/dist/core/delta/document.js +177 -0
  496. package/dist/core/delta/document.js.map +1 -0
  497. package/dist/core/delta/scope.js +48 -0
  498. package/dist/core/delta/scope.js.map +1 -0
  499. package/dist/core/delta/select.js +237 -0
  500. package/dist/core/delta/select.js.map +1 -0
  501. package/dist/core/dependencies/dependencies.js +141 -0
  502. package/dist/core/dependencies/dependencies.js.map +1 -0
  503. package/dist/core/dependencies/facts.js +165 -0
  504. package/dist/core/dependencies/facts.js.map +1 -0
  505. package/dist/core/dependencies/fanin.js +88 -0
  506. package/dist/core/dependencies/fanin.js.map +1 -0
  507. package/dist/core/dependencies/graph.js +106 -0
  508. package/dist/core/dependencies/graph.js.map +1 -0
  509. package/dist/core/diff/base-git.js +148 -0
  510. package/dist/core/diff/base-git.js.map +1 -0
  511. package/dist/core/diff/base-state.js +209 -0
  512. package/dist/core/diff/base-state.js.map +1 -0
  513. package/dist/core/diff/current-state.js +96 -0
  514. package/dist/core/diff/current-state.js.map +1 -0
  515. package/dist/core/diff/scope/changed-paths.js +188 -0
  516. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  517. package/dist/core/diff/semantic.js +243 -0
  518. package/dist/core/diff/semantic.js.map +1 -0
  519. package/dist/core/diff/victims.js +247 -0
  520. package/dist/core/diff/victims.js.map +1 -0
  521. package/dist/core/docs.js +7 -64
  522. package/dist/core/docs.js.map +1 -1
  523. package/dist/core/doctor/agents.js +222 -0
  524. package/dist/core/doctor/agents.js.map +1 -0
  525. package/dist/core/doctor/config.js +135 -0
  526. package/dist/core/doctor/config.js.map +1 -0
  527. package/dist/core/doctor/doctor.js +275 -0
  528. package/dist/core/doctor/doctor.js.map +1 -0
  529. package/dist/core/doctor/report.js +13 -0
  530. package/dist/core/doctor/report.js.map +1 -0
  531. package/dist/core/doctor/residue.js +110 -0
  532. package/dist/core/doctor/residue.js.map +1 -0
  533. package/dist/core/document/apply.js +77 -0
  534. package/dist/core/document/apply.js.map +1 -0
  535. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  536. package/dist/core/document/frontmatter.js.map +1 -0
  537. package/dist/core/document/grammar/directives.js +143 -0
  538. package/dist/core/document/grammar/directives.js.map +1 -0
  539. package/dist/core/document/parse.js +263 -0
  540. package/dist/core/document/parse.js.map +1 -0
  541. package/dist/core/document/scenarios.js +221 -0
  542. package/dist/core/document/scenarios.js.map +1 -0
  543. package/dist/core/document/spec.js +241 -0
  544. package/dist/core/document/spec.js.map +1 -0
  545. package/dist/core/{config.js → envelope/config.js} +79 -18
  546. package/dist/core/envelope/config.js.map +1 -0
  547. package/dist/core/envelope/json.js +175 -0
  548. package/dist/core/envelope/json.js.map +1 -0
  549. package/dist/core/{version.js → envelope/version.js} +3 -3
  550. package/dist/core/envelope/version.js.map +1 -0
  551. package/dist/core/explain/families.js +175 -0
  552. package/dist/core/explain/families.js.map +1 -0
  553. package/dist/core/explain/fix-tables.js +178 -0
  554. package/dist/core/explain/fix-tables.js.map +1 -0
  555. package/dist/core/explain/lookup.js +181 -0
  556. package/dist/core/explain/lookup.js.map +1 -0
  557. package/dist/core/explain/refusals.js +49 -0
  558. package/dist/core/explain/refusals.js.map +1 -0
  559. package/dist/core/explain/terms.js +144 -0
  560. package/dist/core/explain/terms.js.map +1 -0
  561. package/dist/core/explore/describe.js +105 -0
  562. package/dist/core/explore/describe.js.map +1 -0
  563. package/dist/core/{explore.js → explore/explore.js} +50 -101
  564. package/dist/core/explore/explore.js.map +1 -0
  565. package/dist/core/fleet-context.js +120 -78
  566. package/dist/core/fleet-context.js.map +1 -1
  567. package/dist/core/gate/checks.js +201 -0
  568. package/dist/core/gate/checks.js.map +1 -0
  569. package/dist/core/gate/partners.js +205 -0
  570. package/dist/core/gate/partners.js.map +1 -0
  571. package/dist/core/gate/report.js +47 -0
  572. package/dist/core/gate/report.js.map +1 -0
  573. package/dist/core/gate/verification.js +113 -0
  574. package/dist/core/gate/verification.js.map +1 -0
  575. package/dist/core/gherkin/digest.js +60 -0
  576. package/dist/core/gherkin/digest.js.map +1 -0
  577. package/dist/core/gherkin/emit.js +177 -0
  578. package/dist/core/gherkin/emit.js.map +1 -0
  579. package/dist/core/gherkin/read.js +76 -0
  580. package/dist/core/gherkin/read.js.map +1 -0
  581. package/dist/core/gherkin/stale.js +234 -0
  582. package/dist/core/gherkin/stale.js.map +1 -0
  583. package/dist/core/gherkin/stamp.js +77 -0
  584. package/dist/core/gherkin/stamp.js.map +1 -0
  585. package/dist/core/gherkin/steps/catalogue.js +154 -0
  586. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  587. package/dist/core/gherkin/steps/inventory.js +98 -0
  588. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  589. package/dist/core/gherkin/steps/phrase.js +72 -0
  590. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  591. package/dist/core/glossary/delta.js +106 -0
  592. package/dist/core/glossary/delta.js.map +1 -0
  593. package/dist/core/glossary/tree.js +86 -0
  594. package/dist/core/glossary/tree.js.map +1 -0
  595. package/dist/core/kernel/concurrency.js.map +1 -0
  596. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  597. package/dist/core/kernel/document-bytes.js.map +1 -0
  598. package/dist/core/kernel/fences.js +42 -0
  599. package/dist/core/kernel/fences.js.map +1 -0
  600. package/dist/core/kernel/ids/capability.js +72 -0
  601. package/dist/core/kernel/ids/capability.js.map +1 -0
  602. package/dist/core/kernel/ids/dirs.js +33 -0
  603. package/dist/core/kernel/ids/dirs.js.map +1 -0
  604. package/dist/core/kernel/ids/feature.js +75 -0
  605. package/dist/core/kernel/ids/feature.js.map +1 -0
  606. package/dist/core/kernel/ids/service.js +156 -0
  607. package/dist/core/kernel/ids/service.js.map +1 -0
  608. package/dist/core/kernel/ids/subsystem.js +63 -0
  609. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  610. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  611. package/dist/core/kernel/path-safety.js.map +1 -0
  612. package/dist/core/kernel/records.js.map +1 -0
  613. package/dist/core/links/backlinks.js +68 -0
  614. package/dist/core/links/backlinks.js.map +1 -0
  615. package/dist/core/links/case.js +74 -0
  616. package/dist/core/links/case.js.map +1 -0
  617. package/dist/core/links/corpus.js +104 -0
  618. package/dist/core/links/corpus.js.map +1 -0
  619. package/dist/core/links/findings.js +110 -0
  620. package/dist/core/links/findings.js.map +1 -0
  621. package/dist/core/links/parse.js +236 -0
  622. package/dist/core/links/parse.js.map +1 -0
  623. package/dist/core/mcp/argv.js +111 -0
  624. package/dist/core/mcp/argv.js.map +1 -0
  625. package/dist/core/mcp/author-tools.js +72 -0
  626. package/dist/core/mcp/author-tools.js.map +1 -0
  627. package/dist/core/mcp/framing.js +46 -0
  628. package/dist/core/mcp/framing.js.map +1 -0
  629. package/dist/core/mcp/protocol.js +280 -0
  630. package/dist/core/mcp/protocol.js.map +1 -0
  631. package/dist/core/mcp/tools.js +314 -0
  632. package/dist/core/mcp/tools.js.map +1 -0
  633. package/dist/core/obligations/obligations.js +106 -0
  634. package/dist/core/obligations/obligations.js.map +1 -0
  635. package/dist/core/openapi/baseline/gate.js +166 -0
  636. package/dist/core/openapi/baseline/gate.js.map +1 -0
  637. package/dist/core/openapi/baseline/plan.js +112 -0
  638. package/dist/core/openapi/baseline/plan.js.map +1 -0
  639. package/dist/core/openapi/baseline/record.js +175 -0
  640. package/dist/core/openapi/baseline/record.js.map +1 -0
  641. package/dist/core/openapi/depth.js +122 -0
  642. package/dist/core/openapi/depth.js.map +1 -0
  643. package/dist/core/openapi/digest.js +189 -0
  644. package/dist/core/openapi/digest.js.map +1 -0
  645. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  646. package/dist/core/openapi/doc.js.map +1 -0
  647. package/dist/core/openapi/merge/components.js +234 -0
  648. package/dist/core/openapi/merge/components.js.map +1 -0
  649. package/dist/core/openapi/merge/error.js +26 -0
  650. package/dist/core/openapi/merge/error.js.map +1 -0
  651. package/dist/core/openapi/merge/markers.js +208 -0
  652. package/dist/core/openapi/merge/markers.js.map +1 -0
  653. package/dist/core/openapi/merge/merge.js +256 -0
  654. package/dist/core/openapi/merge/merge.js.map +1 -0
  655. package/dist/core/openapi/merge/pin.js +128 -0
  656. package/dist/core/openapi/merge/pin.js.map +1 -0
  657. package/dist/core/openspec/decide/capabilities.js +244 -0
  658. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  659. package/dist/core/openspec/decide/dispositions.js +63 -0
  660. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  661. package/dist/core/openspec/decide/renames.js +179 -0
  662. package/dist/core/openspec/decide/renames.js.map +1 -0
  663. package/dist/core/openspec/decide/verdict.js +84 -0
  664. package/dist/core/openspec/decide/verdict.js.map +1 -0
  665. package/dist/core/openspec/inventory.js +221 -0
  666. package/dist/core/openspec/inventory.js.map +1 -0
  667. package/dist/core/openspec/model/mapping.js +76 -0
  668. package/dist/core/openspec/model/mapping.js.map +1 -0
  669. package/dist/core/openspec/model/model.js +65 -0
  670. package/dist/core/openspec/model/model.js.map +1 -0
  671. package/dist/core/openspec/scan/changes.js +235 -0
  672. package/dist/core/openspec/scan/changes.js.map +1 -0
  673. package/dist/core/openspec/scan/config.js +200 -0
  674. package/dist/core/openspec/scan/config.js.map +1 -0
  675. package/dist/core/openspec/scan/shape.js +260 -0
  676. package/dist/core/openspec/scan/shape.js.map +1 -0
  677. package/dist/core/openspec/scan/walk.js +133 -0
  678. package/dist/core/openspec/scan/walk.js.map +1 -0
  679. package/dist/core/openspec/scan/workspace.js +71 -0
  680. package/dist/core/openspec/scan/workspace.js.map +1 -0
  681. package/dist/core/owners/codeowners.js +141 -0
  682. package/dist/core/owners/codeowners.js.map +1 -0
  683. package/dist/core/pack/features.js +120 -0
  684. package/dist/core/pack/features.js.map +1 -0
  685. package/dist/core/pack/joins.js +112 -0
  686. package/dist/core/pack/joins.js.map +1 -0
  687. package/dist/core/pack/living.js +118 -0
  688. package/dist/core/pack/living.js.map +1 -0
  689. package/dist/core/pack/pack.js +76 -0
  690. package/dist/core/pack/pack.js.map +1 -0
  691. package/dist/core/permissions/permissions.js +87 -0
  692. package/dist/core/permissions/permissions.js.map +1 -0
  693. package/dist/core/projection/api.js +84 -0
  694. package/dist/core/projection/api.js.map +1 -0
  695. package/dist/core/projection/arch-slice.js +63 -0
  696. package/dist/core/projection/arch-slice.js.map +1 -0
  697. package/dist/core/projection/events.js +34 -0
  698. package/dist/core/projection/events.js.map +1 -0
  699. package/dist/core/provenance/findings.js +208 -0
  700. package/dist/core/provenance/findings.js.map +1 -0
  701. package/dist/core/provenance/git.js +225 -0
  702. package/dist/core/provenance/git.js.map +1 -0
  703. package/dist/core/provenance/gitq/moves.js +182 -0
  704. package/dist/core/provenance/gitq/moves.js.map +1 -0
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  707. package/dist/core/provenance/sample/scope.js +238 -0
  708. package/dist/core/provenance/sample/scope.js.map +1 -0
  709. package/dist/core/provenance/sample/sections.js +61 -0
  710. package/dist/core/provenance/sample/sections.js.map +1 -0
  711. package/dist/core/provenance/sources.js +263 -0
  712. package/dist/core/provenance/sources.js.map +1 -0
  713. package/dist/core/provenance/stamp.js +232 -0
  714. package/dist/core/provenance/stamp.js.map +1 -0
  715. package/dist/core/provenance/walk.js +157 -0
  716. package/dist/core/provenance/walk.js.map +1 -0
  717. package/dist/core/repo/authored/paths.js +192 -0
  718. package/dist/core/repo/authored/paths.js.map +1 -0
  719. package/dist/core/repo/entries.js +126 -0
  720. package/dist/core/repo/entries.js.map +1 -0
  721. package/dist/core/repo/paths.js +236 -0
  722. package/dist/core/repo/paths.js.map +1 -0
  723. package/dist/core/repo/repo.js +248 -0
  724. package/dist/core/repo/repo.js.map +1 -0
  725. package/dist/core/repo/service-target.js +153 -0
  726. package/dist/core/repo/service-target.js.map +1 -0
  727. package/dist/core/repo/state.js +58 -0
  728. package/dist/core/repo/state.js.map +1 -0
  729. package/dist/core/repo/tree/find.js +52 -0
  730. package/dist/core/repo/tree/find.js.map +1 -0
  731. package/dist/core/repo/tree/fs.js +64 -0
  732. package/dist/core/repo/tree/fs.js.map +1 -0
  733. package/dist/core/repo/tree/marker.js +61 -0
  734. package/dist/core/repo/tree/marker.js.map +1 -0
  735. package/dist/core/repo/tree/views.js +172 -0
  736. package/dist/core/repo/tree/views.js.map +1 -0
  737. package/dist/core/repo/tree/walk.js +234 -0
  738. package/dist/core/repo/tree/walk.js.map +1 -0
  739. package/dist/core/results.js +99 -2
  740. package/dist/core/results.js.map +1 -1
  741. package/dist/core/scaffold/landscape.js +131 -0
  742. package/dist/core/scaffold/landscape.js.map +1 -0
  743. package/dist/core/scaffold/readme.js +84 -0
  744. package/dist/core/scaffold/readme.js.map +1 -0
  745. package/dist/core/staging/commit.js +241 -0
  746. package/dist/core/staging/commit.js.map +1 -0
  747. package/dist/core/staging/interrupted.js +96 -0
  748. package/dist/core/staging/interrupted.js.map +1 -0
  749. package/dist/core/staging/lock.js +206 -0
  750. package/dist/core/staging/lock.js.map +1 -0
  751. package/dist/core/staging/recovery/finding.js +35 -0
  752. package/dist/core/staging/recovery/finding.js.map +1 -0
  753. package/dist/core/staging/recovery/intent.js +98 -0
  754. package/dist/core/staging/recovery/intent.js.map +1 -0
  755. package/dist/core/staging/recovery/recover.js +126 -0
  756. package/dist/core/staging/recovery/recover.js.map +1 -0
  757. package/dist/core/staging/recovery/residue.js +62 -0
  758. package/dist/core/staging/recovery/residue.js.map +1 -0
  759. package/dist/core/staging/snapshot.js +202 -0
  760. package/dist/core/staging/snapshot.js.map +1 -0
  761. package/dist/core/staging/txn/forward.js +171 -0
  762. package/dist/core/staging/txn/forward.js.map +1 -0
  763. package/dist/core/staging/txn/journal.js +139 -0
  764. package/dist/core/staging/txn/journal.js.map +1 -0
  765. package/dist/core/staging/txn/transaction.js +75 -0
  766. package/dist/core/staging/txn/transaction.js.map +1 -0
  767. package/dist/core/staging/writes.js +113 -0
  768. package/dist/core/staging/writes.js.map +1 -0
  769. package/dist/core/status/actions/execution.js +49 -0
  770. package/dist/core/status/actions/execution.js.map +1 -0
  771. package/dist/core/status/contracts.js +102 -0
  772. package/dist/core/status/contracts.js.map +1 -0
  773. package/dist/core/status/feature/artifacts.js +171 -0
  774. package/dist/core/status/feature/artifacts.js.map +1 -0
  775. package/dist/core/status/feature/feature.js +207 -0
  776. package/dist/core/status/feature/feature.js.map +1 -0
  777. package/dist/core/status/feature/next.js +150 -0
  778. package/dist/core/status/feature/next.js.map +1 -0
  779. package/dist/core/status/feature/state.js +21 -0
  780. package/dist/core/status/feature/state.js.map +1 -0
  781. package/dist/core/status/fleet/fleet.js +187 -0
  782. package/dist/core/status/fleet/fleet.js.map +1 -0
  783. package/dist/core/status/fleet/next.js +207 -0
  784. package/dist/core/status/fleet/next.js.map +1 -0
  785. package/dist/core/status/interrupted.js +97 -0
  786. package/dist/core/status/interrupted.js.map +1 -0
  787. package/dist/core/status/report.js +31 -0
  788. package/dist/core/status/report.js.map +1 -0
  789. package/dist/core/status/scan.js +41 -0
  790. package/dist/core/status/scan.js.map +1 -0
  791. package/dist/core/status/verification.js +201 -0
  792. package/dist/core/status/verification.js.map +1 -0
  793. package/dist/core/usecases/capability.js +169 -0
  794. package/dist/core/usecases/capability.js.map +1 -0
  795. package/dist/core/usecases/delta/claims.js +116 -0
  796. package/dist/core/usecases/delta/claims.js.map +1 -0
  797. package/dist/core/usecases/delta/flows.js +103 -0
  798. package/dist/core/usecases/delta/flows.js.map +1 -0
  799. package/dist/core/usecases/delta/overlay.js +235 -0
  800. package/dist/core/usecases/delta/overlay.js.map +1 -0
  801. package/dist/core/usecases/fleet.js +137 -0
  802. package/dist/core/usecases/fleet.js.map +1 -0
  803. package/dist/core/usecases/operations.js +111 -0
  804. package/dist/core/usecases/operations.js.map +1 -0
  805. package/dist/core/usecases/place.js +44 -0
  806. package/dist/core/usecases/place.js.map +1 -0
  807. package/dist/core/usecases/touch.js +101 -0
  808. package/dist/core/usecases/touch.js.map +1 -0
  809. package/dist/core/verify/answers.js +173 -0
  810. package/dist/core/verify/answers.js.map +1 -0
  811. package/dist/core/verify/build.js +150 -0
  812. package/dist/core/verify/build.js.map +1 -0
  813. package/dist/core/verify/checklist.js +201 -0
  814. package/dist/core/verify/checklist.js.map +1 -0
  815. package/dist/core/verify/claims/identity.js +61 -0
  816. package/dist/core/verify/claims/identity.js.map +1 -0
  817. package/dist/core/verify/cross/diff.js +97 -0
  818. package/dist/core/verify/cross/diff.js.map +1 -0
  819. package/dist/core/verify/evidence/contract.js +169 -0
  820. package/dist/core/verify/evidence/contract.js.map +1 -0
  821. package/dist/core/verify/file.js +257 -0
  822. package/dist/core/verify/file.js.map +1 -0
  823. package/dist/core/verify/pins/lint.js +157 -0
  824. package/dist/core/verify/pins/lint.js.map +1 -0
  825. package/dist/core/verify/pins/pin.js +95 -0
  826. package/dist/core/verify/pins/pin.js.map +1 -0
  827. package/dist/core/verify/record.js +142 -0
  828. package/dist/core/verify/record.js.map +1 -0
  829. package/dist/core/verify/store/commit.js +89 -0
  830. package/dist/core/verify/store/commit.js.map +1 -0
  831. package/dist/core/verify/store/render.js +57 -0
  832. package/dist/core/verify/store/render.js.map +1 -0
  833. package/dist/core/vocabulary/health.js +91 -0
  834. package/dist/core/vocabulary/health.js.map +1 -0
  835. package/dist/core/vocabulary/issue.js +55 -0
  836. package/dist/core/vocabulary/issue.js.map +1 -0
  837. package/dist/core/vocabulary/maturity.js +145 -0
  838. package/dist/core/vocabulary/maturity.js.map +1 -0
  839. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  840. package/dist/core/vocabulary/report.js.map +1 -0
  841. package/dist/core/vocabulary/steps.js +266 -0
  842. package/dist/core/vocabulary/steps.js.map +1 -0
  843. package/dist/core/workspace/discover.js +239 -0
  844. package/dist/core/workspace/discover.js.map +1 -0
  845. package/dist/core/workspace/render.js +52 -0
  846. package/dist/core/workspace/render.js.map +1 -0
  847. package/examples/README.md +189 -0
  848. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  849. package/examples/docs/architecture/capabilities.yaml +31 -0
  850. package/examples/docs/architecture/landscape.likec4 +280 -0
  851. package/examples/docs/architecture/obligations.yaml +23 -0
  852. package/examples/docs/architecture/permissions.yaml +50 -0
  853. package/examples/docs/architecture/subsystems.likec4 +11 -0
  854. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  855. package/examples/docs/capabilities/checkout/spec.md +55 -0
  856. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  857. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  858. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  859. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  860. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  863. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  864. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  865. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  866. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  867. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  868. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  869. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  870. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  871. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  872. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  873. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  874. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  875. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  876. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  877. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  878. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  879. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  882. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  883. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  884. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  885. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  886. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  887. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  888. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  889. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  890. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  891. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  896. package/examples/docs/glossary/order.md +15 -0
  897. package/examples/docs/glossary/payments/authorization.md +14 -0
  898. package/examples/docs/likec4.config.json +9 -0
  899. package/examples/docs/loam.json +3 -0
  900. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  901. package/examples/docs/services/checkout-web/spec.md +91 -0
  902. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  903. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  904. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  905. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  906. package/examples/docs/services/order-service/arch.spec.md +127 -0
  907. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  908. package/examples/docs/services/order-service/health.yaml +57 -0
  909. package/examples/docs/services/order-service/model.likec4 +138 -0
  910. package/examples/docs/services/order-service/openapi.yaml +257 -0
  911. package/examples/docs/services/order-service/runbook.md +62 -0
  912. package/examples/docs/services/order-service/spec.md +134 -0
  913. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  914. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  915. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  916. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  917. package/examples/docs/services/payment-service/health.yaml +39 -0
  918. package/examples/docs/services/payment-service/model.likec4 +67 -0
  919. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  920. package/examples/docs/services/payment-service/runbook.md +37 -0
  921. package/examples/docs/services/payment-service/spec.md +88 -0
  922. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  923. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  924. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  925. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  926. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  927. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  928. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  929. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  930. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  931. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  932. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  933. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  934. package/examples/docs/services/platform/subsystem.yaml +2 -0
  935. package/examples/fleet.yaml +57 -0
  936. package/package.json +19 -6
  937. package/dist/commands/adopt.js +0 -240
  938. package/dist/commands/adopt.js.map +0 -1
  939. package/dist/commands/archive.js +0 -1313
  940. package/dist/commands/archive.js.map +0 -1
  941. package/dist/commands/delta.js +0 -388
  942. package/dist/commands/delta.js.map +0 -1
  943. package/dist/commands/docs-repo-gate.js.map +0 -1
  944. package/dist/commands/format.js +0 -17
  945. package/dist/commands/format.js.map +0 -1
  946. package/dist/commands/gherkin.js +0 -309
  947. package/dist/commands/gherkin.js.map +0 -1
  948. package/dist/commands/init.js.map +0 -1
  949. package/dist/commands/list.js +0 -295
  950. package/dist/commands/list.js.map +0 -1
  951. package/dist/commands/migrate-openspec.js +0 -950
  952. package/dist/commands/migrate-openspec.js.map +0 -1
  953. package/dist/commands/new.js +0 -413
  954. package/dist/commands/new.js.map +0 -1
  955. package/dist/commands/rebase.js +0 -356
  956. package/dist/commands/rebase.js.map +0 -1
  957. package/dist/commands/show.js.map +0 -1
  958. package/dist/commands/status.js +0 -262
  959. package/dist/commands/status.js.map +0 -1
  960. package/dist/commands/unarchive.js.map +0 -1
  961. package/dist/commands/validate.js +0 -1773
  962. package/dist/commands/validate.js.map +0 -1
  963. package/dist/commands/verify.js +0 -775
  964. package/dist/commands/verify.js.map +0 -1
  965. package/dist/commands/vouch.js +0 -421
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2173
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js.map +0 -1
  970. package/dist/core/arch.js +0 -130
  971. package/dist/core/arch.js.map +0 -1
  972. package/dist/core/asyncapi.js.map +0 -1
  973. package/dist/core/brief.js +0 -755
  974. package/dist/core/brief.js.map +0 -1
  975. package/dist/core/coherence.js +0 -577
  976. package/dist/core/coherence.js.map +0 -1
  977. package/dist/core/concurrency.js.map +0 -1
  978. package/dist/core/config.js.map +0 -1
  979. package/dist/core/delta.js +0 -476
  980. package/dist/core/delta.js.map +0 -1
  981. package/dist/core/dependencies.js +0 -356
  982. package/dist/core/dependencies.js.map +0 -1
  983. package/dist/core/doctor.js +0 -596
  984. package/dist/core/doctor.js.map +0 -1
  985. package/dist/core/document-bytes.js.map +0 -1
  986. package/dist/core/explore.js.map +0 -1
  987. package/dist/core/frontmatter.js.map +0 -1
  988. package/dist/core/gherkin.js +0 -463
  989. package/dist/core/gherkin.js.map +0 -1
  990. package/dist/core/health.js +0 -67
  991. package/dist/core/health.js.map +0 -1
  992. package/dist/core/ids.js +0 -122
  993. package/dist/core/ids.js.map +0 -1
  994. package/dist/core/issue.js +0 -9
  995. package/dist/core/issue.js.map +0 -1
  996. package/dist/core/json.js +0 -74
  997. package/dist/core/json.js.map +0 -1
  998. package/dist/core/likec4.js +0 -489
  999. package/dist/core/likec4.js.map +0 -1
  1000. package/dist/core/maturity.js +0 -66
  1001. package/dist/core/maturity.js.map +0 -1
  1002. package/dist/core/openapi-merge.js +0 -552
  1003. package/dist/core/openapi-merge.js.map +0 -1
  1004. package/dist/core/openapi.js.map +0 -1
  1005. package/dist/core/openspec-inventory.js +0 -1440
  1006. package/dist/core/openspec-inventory.js.map +0 -1
  1007. package/dist/core/path-safety.js.map +0 -1
  1008. package/dist/core/provenance.js +0 -787
  1009. package/dist/core/provenance.js.map +0 -1
  1010. package/dist/core/records.js.map +0 -1
  1011. package/dist/core/repo.js +0 -438
  1012. package/dist/core/repo.js.map +0 -1
  1013. package/dist/core/report.js.map +0 -1
  1014. package/dist/core/spec.js +0 -505
  1015. package/dist/core/spec.js.map +0 -1
  1016. package/dist/core/staging.js +0 -890
  1017. package/dist/core/staging.js.map +0 -1
  1018. package/dist/core/status.js +0 -1132
  1019. package/dist/core/status.js.map +0 -1
  1020. package/dist/core/steps.js +0 -72
  1021. package/dist/core/steps.js.map +0 -1
  1022. package/dist/core/verify.js +0 -807
  1023. package/dist/core/verify.js.map +0 -1
  1024. package/dist/core/version.js.map +0 -1
  1025. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1026. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,237 @@
1
+ /**
2
+ * What each delta requirement SELECTS in the living document, and whether the
3
+ * merge would do what its author meant.
4
+ *
5
+ * This is the half that needs both documents at once, which is why it is not in
6
+ * `./document.ts`. Every refusal here is a shape the merge would apply cleanly
7
+ * and silently: MODIFIED of a requirement that does not exist is merged as a
8
+ * creation, ADDED of one that does exist REPLACES it scenarios and all, and a
9
+ * stale `Based-On:` means someone landed a change in between that this delta
10
+ * would overwrite with text written against a document that no longer exists.
11
+ */
12
+ import {} from "../vocabulary/issue.js";
13
+ import { isRequirementsHeading } from "../document/parse.js";
14
+ import { basedOnDeclarations, REQUIREMENT_DIGEST_RE, requirementDigest, } from "../document/spec.js";
15
+ import {} from "./claims.js";
16
+ import {} from "./scope.js";
17
+ export async function selectionIssues(scope, reqs, living, claims) {
18
+ const { featureId, subject, where, livingDoc } = scope;
19
+ const issues = [];
20
+ /**
21
+ * The pin, against the living requirement this delta actually selects.
22
+ *
23
+ * This is the check `delta.modified-conflict` cannot be: that one names
24
+ * the other feature while BOTH are in flight, and the collision that
25
+ * happens on a fleet outlives the window. A archives, leaves `features/`,
26
+ * and stops being an active claim; B revalidates GREEN, and its MODIFIED —
27
+ * carrying the full text it wrote against the pre-A document — replaces
28
+ * A's requirement wholesale, scenarios and all, with `+0 ~1 -0` and exit
29
+ * 0. Nothing downstream can even detect it afterwards: `unarchive A`
30
+ * refuses `snapshot-stale`, and `--force` takes B out with it.
31
+ *
32
+ * A digest of what the author read closes that by not depending on
33
+ * timing at all. Stale is an ERROR and gates, because there is no reading
34
+ * of a stale delta under which the merge is what its author meant.
35
+ * Missing is a warning that GATES: the document is legal — a delta
36
+ * adopted from OpenSpec never had the line — but an unpinned MODIFIED or
37
+ * REMOVED is exactly the timing hole the pin exists to close, and letting
38
+ * it through archived the silent rollback at exit 0. The migrated corpus
39
+ * is why this stays a warning rather than an error, and why the refusal
40
+ * is not an outage: `loam rebase <feat>` pins every delta in one command,
41
+ * and `--approve` remains the way to say the unpinned merge is meant.
42
+ */
43
+ const checkBaseline = (r, selected) => {
44
+ // A pin the document pass already refused is not also stale: that would
45
+ // send its author to `loam rebase` for a problem rebase does not fix.
46
+ const declared = basedOnDeclarations(r);
47
+ if (declared.length > 1)
48
+ return;
49
+ if (r.basedOn !== undefined && !REQUIREMENT_DIGEST_RE.test(r.basedOn))
50
+ return;
51
+ const current = requirementDigest(selected);
52
+ if (r.basedOn === undefined) {
53
+ issues.push({
54
+ severity: "warn",
55
+ gates: true,
56
+ code: "delta.baseline-missing",
57
+ subject,
58
+ message: `${where}: ${r.kind} requirement '${r.name}' carries no Based-On, so nothing can say whether the living text moved since this delta was written — merging it would replace whatever landed in between. Run \`loam rebase ${featureId}\` to pin it (Based-On: ${current}), or archive with --approve to merge unpinned deliberately.`,
59
+ });
60
+ return;
61
+ }
62
+ if (r.basedOn === current)
63
+ return;
64
+ issues.push({
65
+ severity: "error",
66
+ code: "delta.baseline-stale",
67
+ subject,
68
+ message: `${where}: ${r.kind} requirement '${r.name}' was written against living version ${r.basedOn}, but the ${livingDoc} now holds ${current} — someone landed a change to it in between. ` +
69
+ (r.kind === "MODIFIED"
70
+ ? `This MODIFIED carries its FULL new text, so merging it would replace theirs outright.`
71
+ : `Merging this REMOVED would delete what they landed.`) +
72
+ ` Re-read the living requirement, fold in what you still mean, then run \`loam rebase ${featureId}\`.`,
73
+ });
74
+ };
75
+ for (const r of reqs) {
76
+ // BASE in a delta file means the requirement is under no delta section, so
77
+ // the merge skips it. Reported rather than merged: deciding that `## Behavior`
78
+ // means ADDED would be archive guessing at intent, and it would guess wrong
79
+ // for every prose section that quotes a requirement as documentation. A
80
+ // requirement above every heading has no heading to name, and no claim to be
81
+ // written in the delta grammar at all — same judgement as `## Requirements`.
82
+ //
83
+ // A warning that GATES archive — the one check where the two axes
84
+ // diverge. Severity says whether the document is valid: this shape is
85
+ // legal OpenSpec, so an error would fail `loam validate` on every
86
+ // adopted repo whose active deltas still use it. Gating says whether
87
+ // the merge is safe: it is not — the merge would silently drop authored
88
+ // content, the exact loss this module exists to prevent — so archive
89
+ // refuses, and `--approve` remains the way to say the loss is meant.
90
+ if (r.kind === "BASE") {
91
+ if (r.section !== undefined && !isRequirementsHeading(r.section)) {
92
+ issues.push({
93
+ severity: "warn",
94
+ gates: true,
95
+ code: "delta.requirement-not-merged",
96
+ subject,
97
+ message: `${where}: requirement '${r.name}' sits under '${r.section}', which is not a delta section — archive will NOT merge it. Move it under '## ADDED Requirements' (or MODIFIED/REMOVED), or drop it if it is documentation.`,
98
+ });
99
+ }
100
+ continue;
101
+ }
102
+ if (r.kind === "ADDED") {
103
+ const idMatches = r.id === undefined ? [] : (living.byId.get(r.id) ?? []);
104
+ const nameMatches = living.byName.get(r.name) ?? [];
105
+ const nameSelectsOther = r.id !== undefined && nameMatches.some((candidate) => candidate.id !== r.id);
106
+ if (nameSelectsOther) {
107
+ issues.push({
108
+ severity: "error",
109
+ code: "delta.requirement-identity-collision",
110
+ subject,
111
+ message: `${where}: ADDED requirement '${r.name}' carries Requirement-ID '${r.id}', but that heading already identifies a different living requirement — ID and name cannot select different identities`,
112
+ });
113
+ continue;
114
+ }
115
+ if ((r.id === undefined && living.names.has(r.name)) || idMatches.length > 0) {
116
+ issues.push({
117
+ severity: "error",
118
+ code: "delta.added-duplicate",
119
+ subject,
120
+ message: r.id === undefined
121
+ ? `${where}: ADDED requirement '${r.name}' already exists in the ${livingDoc} — the merge would REPLACE it, scenarios and all. Use MODIFIED, or rename.`
122
+ : `${where}: ADDED requirement '${r.name}' uses Requirement-ID '${r.id}', which already exists in the ${livingDoc} — use MODIFIED to change that identity`,
123
+ });
124
+ continue;
125
+ }
126
+ // Merge identity is exact string equality (applyRequirementDelta), so a
127
+ // name that differs only in case slips past the duplicate check above and
128
+ // lands as a SECOND requirement next to the living one. A warning, not an
129
+ // error: the author may genuinely mean a distinct requirement, but with
130
+ // LLM authors the case drift is the likely story. Never fires together
131
+ // with added-duplicate — an exact match continues before this runs.
132
+ const near = living.folded.get(r.name.toLowerCase());
133
+ if (near !== undefined) {
134
+ issues.push({
135
+ severity: "warn",
136
+ code: "delta.added-near-duplicate",
137
+ subject,
138
+ message: `${where}: ADDED requirement '${r.name}' differs only in case from living requirement '${near}' — the merge matches names exactly, so both would coexist. Match the living spelling and use MODIFIED, or pick a distinct name.`,
139
+ });
140
+ }
141
+ const other = await claims.added(scope, r);
142
+ if (other !== undefined) {
143
+ issues.push({
144
+ severity: "warn",
145
+ code: "delta.added-conflict",
146
+ subject,
147
+ message: `${where}: requirement '${r.name}' is also added by ${other} — whichever archives first lands it, and the second archive is refused (delta.added-duplicate) unless --approve`,
148
+ });
149
+ }
150
+ continue;
151
+ }
152
+ // MODIFIED vs MODIFIED on ONE living requirement. The ADDED case has
153
+ // been caught since delta.added-conflict: two features adding the same
154
+ // name collide loudly, because the second archive is refused. Two
155
+ // features CHANGING the same requirement collide in total silence —
156
+ // both deltas apply cleanly, and whichever archives second replaces
157
+ // the first's text, scenarios and all, with a version written against
158
+ // a document that no longer exists. A warning, not a gate: the second
159
+ // author may well be rewriting on purpose, and only they can say.
160
+ const alsoChanged = await claims.changed(scope, r);
161
+ if (alsoChanged !== undefined) {
162
+ issues.push({
163
+ severity: "warn",
164
+ code: "delta.modified-conflict",
165
+ subject,
166
+ message: `${where}: ${r.kind} requirement '${r.name}' is also changed by ${alsoChanged} — both deltas apply cleanly, so whichever archives second REPLACES the other's text wholesale. Agree on one owner, or fold the two changes together.`,
167
+ });
168
+ }
169
+ if (r.id !== undefined) {
170
+ const idMatches = living.byId.get(r.id) ?? [];
171
+ const nameMatches = living.byName.get(r.name) ?? [];
172
+ const nameSelectsOther = nameMatches.some((candidate) => candidate.id !== r.id);
173
+ // The second disjunct is defensive rather than reachable: a living
174
+ // requirement under this heading that the ID did NOT select must carry
175
+ // some other ID, which is what `nameSelectsOther` already says. It is
176
+ // kept because it states the rule the reader is being told — ID and
177
+ // heading must select the same identity — without depending on that
178
+ // derivation holding after the next edit.
179
+ if (nameSelectsOther || (idMatches.length === 0 && nameMatches.length > 0)) {
180
+ issues.push({
181
+ severity: "error",
182
+ code: "delta.requirement-identity-collision",
183
+ subject,
184
+ message: `${where}: ${r.kind} requirement '${r.name}' carries Requirement-ID '${r.id}', but its ID and heading select different living requirements — fix the ID or heading; archive will not guess`,
185
+ });
186
+ continue;
187
+ }
188
+ // A differing heading is the explicit loam rename mechanism: the
189
+ // stable ID selects the old requirement, MODIFIED supplies its new name.
190
+ //
191
+ // Total in the ID, deliberately: an ID that matches SEVERAL living
192
+ // requirements has already been refused as
193
+ // `delta.living-requirement-id-invalid` (severity error, so archive is
194
+ // gated either way), and the living document is where it gets fixed.
195
+ // Falling through instead added `delta.modified-unknown` — "does not
196
+ // exist … Did you mean ADDED?" — beside it, which is both false and
197
+ // the more actionable-sounding of the two, and it sends the author to
198
+ // edit the delta over a problem that is not in the delta. There is no
199
+ // baseline to check against an ambiguous selection, so the pin is
200
+ // simply not graded until the ambiguity is gone.
201
+ if (idMatches.length > 0) {
202
+ const selected = idMatches.length === 1 ? idMatches[0] : undefined;
203
+ if (selected !== undefined)
204
+ checkBaseline(r, selected);
205
+ continue;
206
+ }
207
+ }
208
+ else if (living.names.has(r.name)) {
209
+ // Twins under one heading are already refused
210
+ // (`delta.living-duplicate-requirement`), and applyRequirementDelta
211
+ // takes the first either way — so the pin is compared against the
212
+ // same requirement the merge would rewrite.
213
+ checkBaseline(r, living.byName.get(r.name)[0]);
214
+ continue;
215
+ }
216
+ const other = await claims.added(scope, r);
217
+ if (other !== undefined) {
218
+ issues.push({
219
+ severity: "warn",
220
+ code: r.kind === "MODIFIED" ? "delta.modified-pending" : "delta.removed-pending",
221
+ subject,
222
+ message: `${where}: ${r.kind} requirement '${r.name}' is not in the ${livingDoc} yet — ${other} introduces it. Archive ${other} first.`,
223
+ });
224
+ continue;
225
+ }
226
+ issues.push({
227
+ severity: "error",
228
+ code: r.kind === "MODIFIED" ? "delta.modified-unknown" : "delta.removed-unknown",
229
+ subject,
230
+ message: r.kind === "MODIFIED"
231
+ ? `${where}: MODIFIED requirement '${r.name}' does not exist in the ${livingDoc} — the merge would create it. Did you mean ADDED?`
232
+ : `${where}: REMOVED requirement '${r.name}' does not exist in the ${livingDoc} — there is nothing to remove`,
233
+ });
234
+ }
235
+ return issues;
236
+ }
237
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../../src/core/delta/select.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What each delta requirement SELECTS in the living document, and whether the\n * merge would do what its author meant.\n *\n * This is the half that needs both documents at once, which is why it is not in\n * `./document.ts`. Every refusal here is a shape the merge would apply cleanly\n * and silently: MODIFIED of a requirement that does not exist is merged as a\n * creation, ADDED of one that does exist REPLACES it scenarios and all, and a\n * stale `Based-On:` means someone landed a change in between that this delta\n * would overwrite with text written against a document that no longer exists.\n */\nimport { type Issue } from \"../vocabulary/issue.js\";\nimport { isRequirementsHeading } from \"../document/parse.js\";\nimport {\n basedOnDeclarations,\n REQUIREMENT_DIGEST_RE,\n requirementDigest,\n type Requirement,\n} from \"../document/spec.js\";\nimport { type ClaimLookup } from \"./claims.js\";\nimport { type DeltaScope, type LivingIndex } from \"./scope.js\";\n\nexport async function selectionIssues(\n scope: DeltaScope,\n reqs: Requirement[],\n living: LivingIndex,\n claims: ClaimLookup,\n): Promise<Issue[]> {\n const { featureId, subject, where, livingDoc } = scope;\n const issues: Issue[] = [];\n /**\n * The pin, against the living requirement this delta actually selects.\n *\n * This is the check `delta.modified-conflict` cannot be: that one names\n * the other feature while BOTH are in flight, and the collision that\n * happens on a fleet outlives the window. A archives, leaves `features/`,\n * and stops being an active claim; B revalidates GREEN, and its MODIFIED —\n * carrying the full text it wrote against the pre-A document — replaces\n * A's requirement wholesale, scenarios and all, with `+0 ~1 -0` and exit\n * 0. Nothing downstream can even detect it afterwards: `unarchive A`\n * refuses `snapshot-stale`, and `--force` takes B out with it.\n *\n * A digest of what the author read closes that by not depending on\n * timing at all. Stale is an ERROR and gates, because there is no reading\n * of a stale delta under which the merge is what its author meant.\n * Missing is a warning that GATES: the document is legal — a delta\n * adopted from OpenSpec never had the line — but an unpinned MODIFIED or\n * REMOVED is exactly the timing hole the pin exists to close, and letting\n * it through archived the silent rollback at exit 0. The migrated corpus\n * is why this stays a warning rather than an error, and why the refusal\n * is not an outage: `loam rebase <feat>` pins every delta in one command,\n * and `--approve` remains the way to say the unpinned merge is meant.\n */\n const checkBaseline = (r: Requirement, selected: Requirement): void => {\n // A pin the document pass already refused is not also stale: that would\n // send its author to `loam rebase` for a problem rebase does not fix.\n const declared = basedOnDeclarations(r);\n if (declared.length > 1) return;\n if (r.basedOn !== undefined && !REQUIREMENT_DIGEST_RE.test(r.basedOn)) return;\n const current = requirementDigest(selected);\n if (r.basedOn === undefined) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"delta.baseline-missing\",\n subject,\n message: `${where}: ${r.kind} requirement '${r.name}' carries no Based-On, so nothing can say whether the living text moved since this delta was written — merging it would replace whatever landed in between. Run \\`loam rebase ${featureId}\\` to pin it (Based-On: ${current}), or archive with --approve to merge unpinned deliberately.`,\n });\n return;\n }\n if (r.basedOn === current) return;\n issues.push({\n severity: \"error\",\n code: \"delta.baseline-stale\",\n subject,\n message:\n `${where}: ${r.kind} requirement '${r.name}' was written against living version ${r.basedOn}, but the ${livingDoc} now holds ${current} — someone landed a change to it in between. ` +\n (r.kind === \"MODIFIED\"\n ? `This MODIFIED carries its FULL new text, so merging it would replace theirs outright.`\n : `Merging this REMOVED would delete what they landed.`) +\n ` Re-read the living requirement, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n };\n\n for (const r of reqs) {\n // BASE in a delta file means the requirement is under no delta section, so\n // the merge skips it. Reported rather than merged: deciding that `## Behavior`\n // means ADDED would be archive guessing at intent, and it would guess wrong\n // for every prose section that quotes a requirement as documentation. A\n // requirement above every heading has no heading to name, and no claim to be\n // written in the delta grammar at all — same judgement as `## Requirements`.\n //\n // A warning that GATES archive — the one check where the two axes\n // diverge. Severity says whether the document is valid: this shape is\n // legal OpenSpec, so an error would fail `loam validate` on every\n // adopted repo whose active deltas still use it. Gating says whether\n // the merge is safe: it is not — the merge would silently drop authored\n // content, the exact loss this module exists to prevent — so archive\n // refuses, and `--approve` remains the way to say the loss is meant.\n if (r.kind === \"BASE\") {\n if (r.section !== undefined && !isRequirementsHeading(r.section)) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"delta.requirement-not-merged\",\n subject,\n message: `${where}: requirement '${r.name}' sits under '${r.section}', which is not a delta section — archive will NOT merge it. Move it under '## ADDED Requirements' (or MODIFIED/REMOVED), or drop it if it is documentation.`,\n });\n }\n continue;\n }\n\n if (r.kind === \"ADDED\") {\n const idMatches = r.id === undefined ? [] : (living.byId.get(r.id) ?? []);\n const nameMatches = living.byName.get(r.name) ?? [];\n const nameSelectsOther = r.id !== undefined && nameMatches.some((candidate) => candidate.id !== r.id);\n if (nameSelectsOther) {\n issues.push({\n severity: \"error\",\n code: \"delta.requirement-identity-collision\",\n subject,\n message: `${where}: ADDED requirement '${r.name}' carries Requirement-ID '${r.id}', but that heading already identifies a different living requirement — ID and name cannot select different identities`,\n });\n continue;\n }\n if ((r.id === undefined && living.names.has(r.name)) || idMatches.length > 0) {\n issues.push({\n severity: \"error\",\n code: \"delta.added-duplicate\",\n subject,\n message: r.id === undefined\n ? `${where}: ADDED requirement '${r.name}' already exists in the ${livingDoc} — the merge would REPLACE it, scenarios and all. Use MODIFIED, or rename.`\n : `${where}: ADDED requirement '${r.name}' uses Requirement-ID '${r.id}', which already exists in the ${livingDoc} — use MODIFIED to change that identity`,\n });\n continue;\n }\n // Merge identity is exact string equality (applyRequirementDelta), so a\n // name that differs only in case slips past the duplicate check above and\n // lands as a SECOND requirement next to the living one. A warning, not an\n // error: the author may genuinely mean a distinct requirement, but with\n // LLM authors the case drift is the likely story. Never fires together\n // with added-duplicate — an exact match continues before this runs.\n const near = living.folded.get(r.name.toLowerCase());\n if (near !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"delta.added-near-duplicate\",\n subject,\n message: `${where}: ADDED requirement '${r.name}' differs only in case from living requirement '${near}' — the merge matches names exactly, so both would coexist. Match the living spelling and use MODIFIED, or pick a distinct name.`,\n });\n }\n const other = await claims.added(scope, r);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"delta.added-conflict\",\n subject,\n message: `${where}: requirement '${r.name}' is also added by ${other} — whichever archives first lands it, and the second archive is refused (delta.added-duplicate) unless --approve`,\n });\n }\n continue;\n }\n\n // MODIFIED vs MODIFIED on ONE living requirement. The ADDED case has\n // been caught since delta.added-conflict: two features adding the same\n // name collide loudly, because the second archive is refused. Two\n // features CHANGING the same requirement collide in total silence —\n // both deltas apply cleanly, and whichever archives second replaces\n // the first's text, scenarios and all, with a version written against\n // a document that no longer exists. A warning, not a gate: the second\n // author may well be rewriting on purpose, and only they can say.\n const alsoChanged = await claims.changed(scope, r);\n if (alsoChanged !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"delta.modified-conflict\",\n subject,\n message: `${where}: ${r.kind} requirement '${r.name}' is also changed by ${alsoChanged} — both deltas apply cleanly, so whichever archives second REPLACES the other's text wholesale. Agree on one owner, or fold the two changes together.`,\n });\n }\n\n if (r.id !== undefined) {\n const idMatches = living.byId.get(r.id) ?? [];\n const nameMatches = living.byName.get(r.name) ?? [];\n const nameSelectsOther = nameMatches.some((candidate) => candidate.id !== r.id);\n // The second disjunct is defensive rather than reachable: a living\n // requirement under this heading that the ID did NOT select must carry\n // some other ID, which is what `nameSelectsOther` already says. It is\n // kept because it states the rule the reader is being told — ID and\n // heading must select the same identity — without depending on that\n // derivation holding after the next edit.\n if (nameSelectsOther || (idMatches.length === 0 && nameMatches.length > 0)) {\n issues.push({\n severity: \"error\",\n code: \"delta.requirement-identity-collision\",\n subject,\n message: `${where}: ${r.kind} requirement '${r.name}' carries Requirement-ID '${r.id}', but its ID and heading select different living requirements — fix the ID or heading; archive will not guess`,\n });\n continue;\n }\n // A differing heading is the explicit loam rename mechanism: the\n // stable ID selects the old requirement, MODIFIED supplies its new name.\n //\n // Total in the ID, deliberately: an ID that matches SEVERAL living\n // requirements has already been refused as\n // `delta.living-requirement-id-invalid` (severity error, so archive is\n // gated either way), and the living document is where it gets fixed.\n // Falling through instead added `delta.modified-unknown` — \"does not\n // exist … Did you mean ADDED?\" — beside it, which is both false and\n // the more actionable-sounding of the two, and it sends the author to\n // edit the delta over a problem that is not in the delta. There is no\n // baseline to check against an ambiguous selection, so the pin is\n // simply not graded until the ambiguity is gone.\n if (idMatches.length > 0) {\n const selected = idMatches.length === 1 ? idMatches[0] : undefined;\n if (selected !== undefined) checkBaseline(r, selected);\n continue;\n }\n } else if (living.names.has(r.name)) {\n // Twins under one heading are already refused\n // (`delta.living-duplicate-requirement`), and applyRequirementDelta\n // takes the first either way — so the pin is compared against the\n // same requirement the merge would rewrite.\n checkBaseline(r, living.byName.get(r.name)![0]!);\n continue;\n }\n\n const other = await claims.added(scope, r);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: r.kind === \"MODIFIED\" ? \"delta.modified-pending\" : \"delta.removed-pending\",\n subject,\n message: `${where}: ${r.kind} requirement '${r.name}' is not in the ${livingDoc} yet — ${other} introduces it. Archive ${other} first.`,\n });\n continue;\n }\n issues.push({\n severity: \"error\",\n code: r.kind === \"MODIFIED\" ? \"delta.modified-unknown\" : \"delta.removed-unknown\",\n subject,\n message:\n r.kind === \"MODIFIED\"\n ? `${where}: MODIFIED requirement '${r.name}' does not exist in the ${livingDoc} — the merge would create it. Did you mean ADDED?`\n : `${where}: REMOVED requirement '${r.name}' does not exist in the ${livingDoc} — there is nothing to remove`,\n });\n }\n return issues;\n}\n"]}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The active-feature dependency graph: who has to archive before whom.
3
+ *
4
+ * A feature depends on another when it MODIFIES or REMOVES a requirement the
5
+ * other adds, or calls an operation the other introduces — the two ways one
6
+ * delta can only apply after another has landed. This module is the walk that
7
+ * puts `./facts.ts` and `./graph.ts` together; the reasons it attaches to each
8
+ * edge are what makes the answer explainable rather than merely true.
9
+ */
10
+ import { inOrder } from "../kernel/concurrency.js";
11
+ import { FleetContext } from "../fleet-context.js";
12
+ import { compareIds } from "../repo/entries.js";
13
+ import { listFeatures } from "../repo/repo.js";
14
+ import { addOwner, opIndexKey, readFacts, reqIndexKey, reqKeys, } from "./facts.js";
15
+ import { appendReason, compareReasons, dependencyFirstOrder, stronglyConnected } from "./graph.js";
16
+ /** Derive the complete active-feature graph, or one feature and its transitive prerequisites. */
17
+ export async function analyzeDependencies(docsDir, featureId, context = new FleetContext()) {
18
+ const features = await listFeatures(docsDir, {}, context);
19
+ // Bounded, not `Promise.all`: `readFacts` loads the feature's delta.likec4,
20
+ // and each of those is a whole Langium workspace held open until the read
21
+ // finishes — the same resource `validate --all` rations for the same measured
22
+ // reason (see core/kernel/concurrency.ts). This module fanned out over every active
23
+ // feature at once, and it is not only `loam dependencies` that pays: the fleet
24
+ // form of `loam status` calls in here too. `inOrder` returns results in input
25
+ // order, so the graph, the conflict list and their ordering are unchanged.
26
+ const facts = await inOrder(features, (feature) => readFacts(docsDir, feature, context));
27
+ const requirementOwners = new Map();
28
+ // The second index over the SAME identity: who MODIFIES or REMOVES it. Only
29
+ // the first index existed, so the graph could see "two features add the same
30
+ // requirement" and was blind to "two features rewrite the same requirement" —
31
+ // the collision that actually happens on a fleet, and the one whose loser
32
+ // loses their whole authored text without a word from any command.
33
+ const changedOwners = new Map();
34
+ const operationOwners = new Map();
35
+ for (const fact of facts) {
36
+ for (const added of fact.addedRequirements) {
37
+ for (const identity of reqKeys(added.requirement)) {
38
+ const at = { service: added.service, axis: added.axis, identity };
39
+ addOwner(requirementOwners, reqIndexKey(at), at, fact.feature.id);
40
+ }
41
+ }
42
+ for (const changed of fact.changedRequirements) {
43
+ for (const identity of reqKeys(changed.requirement)) {
44
+ const at = { service: changed.service, axis: changed.axis, identity };
45
+ addOwner(changedOwners, reqIndexKey(at), at, fact.feature.id);
46
+ }
47
+ }
48
+ for (const operation of fact.introducedOperations) {
49
+ addOwner(operationOwners, opIndexKey(operation), operation, fact.feature.id);
50
+ }
51
+ }
52
+ const edgeMap = new Map();
53
+ for (const fact of facts) {
54
+ for (const changed of fact.changedRequirements) {
55
+ for (const identity of reqKeys(changed.requirement)) {
56
+ const at = { service: changed.service, axis: changed.axis, identity };
57
+ for (const owner of requirementOwners.get(reqIndexKey(at))?.features ?? []) {
58
+ appendReason(edgeMap, fact.feature.id, owner, { kind: "requirement", ...at });
59
+ }
60
+ }
61
+ }
62
+ for (const required of fact.requiredOperations) {
63
+ for (const owner of operationOwners.get(opIndexKey(required))?.features ?? []) {
64
+ appendReason(edgeMap, fact.feature.id, owner, { kind: "operation", ...required });
65
+ }
66
+ }
67
+ }
68
+ let edges = [...edgeMap.values()]
69
+ .map((edge) => ({ ...edge, reasons: edge.reasons.sort(compareReasons) }))
70
+ .sort((a, b) => compareIds(a.from, b.from) || compareIds(a.to, b.to));
71
+ const conflicts = [];
72
+ for (const [index, change] of [
73
+ [requirementOwners, "added"],
74
+ [changedOwners, "changed"],
75
+ ]) {
76
+ for (const { at, features } of index.values()) {
77
+ if (features.size < 2)
78
+ continue;
79
+ conflicts.push({
80
+ kind: "requirement",
81
+ change,
82
+ service: at.service,
83
+ axis: at.axis,
84
+ identity: at.identity,
85
+ features: [...features].sort(compareIds),
86
+ });
87
+ }
88
+ }
89
+ for (const { at, features } of operationOwners.values()) {
90
+ if (features.size < 2)
91
+ continue;
92
+ conflicts.push({
93
+ kind: "operation",
94
+ // Two features DEFINING the same operationId — the only way an operation
95
+ // collides, since removals address a living slot rather than claim one.
96
+ change: "added",
97
+ service: at.service,
98
+ identity: at.operationId,
99
+ features: [...features].sort(compareIds),
100
+ });
101
+ }
102
+ conflicts.sort((a, b) => compareIds(a.kind, b.kind)
103
+ || compareIds(a.change, b.change)
104
+ || compareIds(a.service, b.service)
105
+ || compareIds(a.axis ?? "", b.axis ?? "")
106
+ || compareIds(a.identity, b.identity));
107
+ // `Set<string>`, not the ids' own brand: `DependencyEdge.from`/`to` are plain
108
+ // strings, and the reassignment below rebuilds the set from a caller's
109
+ // argument.
110
+ let selected = new Set(features.map((feature) => feature.id));
111
+ if (featureId !== undefined) {
112
+ selected = new Set([featureId]);
113
+ let changed = true;
114
+ while (changed) {
115
+ changed = false;
116
+ for (const edge of edges) {
117
+ if (selected.has(edge.from) && !selected.has(edge.to)) {
118
+ selected.add(edge.to);
119
+ changed = true;
120
+ }
121
+ }
122
+ }
123
+ edges = edges.filter((edge) => selected.has(edge.from) && selected.has(edge.to));
124
+ }
125
+ const selectedFeatures = features.filter((feature) => selected.has(feature.id));
126
+ const ids = selectedFeatures.map((feature) => feature.id);
127
+ const nodes = selectedFeatures.map((feature) => ({
128
+ id: feature.id,
129
+ dirName: feature.dirName,
130
+ services: [...feature.services],
131
+ dependsOn: edges.filter((edge) => edge.from === feature.id).map((edge) => edge.to).sort(compareIds),
132
+ }));
133
+ return {
134
+ nodes,
135
+ edges,
136
+ conflicts: conflicts.filter((conflict) => conflict.features.some((id) => selected.has(id))),
137
+ order: dependencyFirstOrder(ids, edges),
138
+ cycles: stronglyConnected(ids, edges),
139
+ };
140
+ }
141
+ //# sourceMappingURL=dependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../../../src/core/dependencies/dependencies.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,OAAO,GAQR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGnG,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAgB,EAChB,SAAkB,EAClB,OAAO,GAAG,IAAI,YAAY,EAAE;IAE5B,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IAC1D,4EAA4E;IAC5E,0EAA0E;IAC1E,8EAA8E;IAC9E,oFAAoF;IACpF,+EAA+E;IAC/E,8EAA8E;IAC9E,2EAA2E;IAC3E,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACzF,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC3D,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,0EAA0E;IAC1E,mEAAmE;IACnE,MAAM,aAAa,GAAG,IAAI,GAAG,EAAyB,CAAC;IACvD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAwB,CAAC;IAExD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3C,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClD,MAAM,EAAE,GAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;gBACzE,QAAQ,CAAC,iBAAiB,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC/C,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBACpD,MAAM,EAAE,GAAU,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;gBAC7E,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAClD,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC/C,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBACpD,MAAM,EAAE,GAAU,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;gBAC7E,KAAK,MAAM,KAAK,IAAI,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;oBAC3E,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;gBAChF,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC/C,KAAK,MAAM,KAAK,IAAI,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;gBAC9E,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;SAC9B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;SACxE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAExE,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI;QAC5B,CAAC,iBAAiB,EAAE,OAAO,CAAC;QAC5B,CAAC,aAAa,EAAE,SAAS,CAAC;KAClB,EAAE,CAAC;QACX,KAAK,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC;gBAAE,SAAS;YAChC,SAAS,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,aAAa;gBACnB,MAAM;gBACN,OAAO,EAAE,EAAE,CAAC,OAAO;gBACnB,IAAI,EAAE,EAAE,CAAC,IAAI;gBACb,QAAQ,EAAE,EAAE,CAAC,QAAQ;gBACrB,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;aACzC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,KAAK,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC;QACxD,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC;YAAE,SAAS;QAChC,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,WAAW;YACjB,yEAAyE;YACzE,wEAAwE;YACxE,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,EAAE,CAAC,OAAO;YACnB,QAAQ,EAAE,EAAE,CAAC,WAAW;YACxB,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;SACzC,CAAC,CAAC;IACL,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACtB,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;WACvB,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;WAC9B,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC;WAChC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;WACtC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEzC,8EAA8E;IAC9E,uEAAuE;IACvE,YAAY;IACZ,IAAI,QAAQ,GAAG,IAAI,GAAG,CAAS,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACtE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,QAAQ,GAAG,IAAI,GAAG,CAAS,CAAC,SAAS,CAAC,CAAC,CAAC;QACxC,IAAI,OAAO,GAAG,IAAI,CAAC;QACnB,OAAO,OAAO,EAAE,CAAC;YACf,OAAO,GAAG,KAAK,CAAC;YAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBACtD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACtB,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,MAAM,GAAG,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAkB,EAAE,CAAC,CAAC;QAC/D,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;KACpG,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,KAAK;QACL,KAAK;QACL,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3F,KAAK,EAAE,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC;QACvC,MAAM,EAAE,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC;KACtC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The active-feature dependency graph: who has to archive before whom.\n *\n * A feature depends on another when it MODIFIES or REMOVES a requirement the\n * other adds, or calls an operation the other introduces — the two ways one\n * delta can only apply after another has landed. This module is the walk that\n * puts `./facts.ts` and `./graph.ts` together; the reasons it attaches to each\n * edge are what makes the answer explainable rather than merely true.\n */\nimport { inOrder } from \"../kernel/concurrency.js\";\nimport { FleetContext } from \"../fleet-context.js\";\nimport { compareIds } from \"../repo/entries.js\";\nimport { listFeatures } from \"../repo/repo.js\";\nimport {\n addOwner,\n opIndexKey,\n readFacts,\n reqIndexKey,\n reqKeys,\n type DependencyEdge,\n type DependencyGraph,\n type DependencyNode,\n type FeatureConflict,\n type OpAt,\n type Owners,\n type ReqAt,\n} from \"./facts.js\";\nimport { appendReason, compareReasons, dependencyFirstOrder, stronglyConnected } from \"./graph.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/** Derive the complete active-feature graph, or one feature and its transitive prerequisites. */\nexport async function analyzeDependencies(\n docsDir: DocsDir,\n featureId?: string,\n context = new FleetContext(),\n): Promise<DependencyGraph> {\n const features = await listFeatures(docsDir, {}, context);\n // Bounded, not `Promise.all`: `readFacts` loads the feature's delta.likec4,\n // and each of those is a whole Langium workspace held open until the read\n // finishes — the same resource `validate --all` rations for the same measured\n // reason (see core/kernel/concurrency.ts). This module fanned out over every active\n // feature at once, and it is not only `loam dependencies` that pays: the fleet\n // form of `loam status` calls in here too. `inOrder` returns results in input\n // order, so the graph, the conflict list and their ordering are unchanged.\n const facts = await inOrder(features, (feature) => readFacts(docsDir, feature, context));\n const requirementOwners = new Map<string, Owners<ReqAt>>();\n // The second index over the SAME identity: who MODIFIES or REMOVES it. Only\n // the first index existed, so the graph could see \"two features add the same\n // requirement\" and was blind to \"two features rewrite the same requirement\" —\n // the collision that actually happens on a fleet, and the one whose loser\n // loses their whole authored text without a word from any command.\n const changedOwners = new Map<string, Owners<ReqAt>>();\n const operationOwners = new Map<string, Owners<OpAt>>();\n\n for (const fact of facts) {\n for (const added of fact.addedRequirements) {\n for (const identity of reqKeys(added.requirement)) {\n const at: ReqAt = { service: added.service, axis: added.axis, identity };\n addOwner(requirementOwners, reqIndexKey(at), at, fact.feature.id);\n }\n }\n for (const changed of fact.changedRequirements) {\n for (const identity of reqKeys(changed.requirement)) {\n const at: ReqAt = { service: changed.service, axis: changed.axis, identity };\n addOwner(changedOwners, reqIndexKey(at), at, fact.feature.id);\n }\n }\n for (const operation of fact.introducedOperations) {\n addOwner(operationOwners, opIndexKey(operation), operation, fact.feature.id);\n }\n }\n\n const edgeMap = new Map<string, DependencyEdge>();\n for (const fact of facts) {\n for (const changed of fact.changedRequirements) {\n for (const identity of reqKeys(changed.requirement)) {\n const at: ReqAt = { service: changed.service, axis: changed.axis, identity };\n for (const owner of requirementOwners.get(reqIndexKey(at))?.features ?? []) {\n appendReason(edgeMap, fact.feature.id, owner, { kind: \"requirement\", ...at });\n }\n }\n }\n for (const required of fact.requiredOperations) {\n for (const owner of operationOwners.get(opIndexKey(required))?.features ?? []) {\n appendReason(edgeMap, fact.feature.id, owner, { kind: \"operation\", ...required });\n }\n }\n }\n\n let edges = [...edgeMap.values()]\n .map((edge) => ({ ...edge, reasons: edge.reasons.sort(compareReasons) }))\n .sort((a, b) => compareIds(a.from, b.from) || compareIds(a.to, b.to));\n\n const conflicts: FeatureConflict[] = [];\n for (const [index, change] of [\n [requirementOwners, \"added\"],\n [changedOwners, \"changed\"],\n ] as const) {\n for (const { at, features } of index.values()) {\n if (features.size < 2) continue;\n conflicts.push({\n kind: \"requirement\",\n change,\n service: at.service,\n axis: at.axis,\n identity: at.identity,\n features: [...features].sort(compareIds),\n });\n }\n }\n for (const { at, features } of operationOwners.values()) {\n if (features.size < 2) continue;\n conflicts.push({\n kind: \"operation\",\n // Two features DEFINING the same operationId — the only way an operation\n // collides, since removals address a living slot rather than claim one.\n change: \"added\",\n service: at.service,\n identity: at.operationId,\n features: [...features].sort(compareIds),\n });\n }\n conflicts.sort((a, b) =>\n compareIds(a.kind, b.kind)\n || compareIds(a.change, b.change)\n || compareIds(a.service, b.service)\n || compareIds(a.axis ?? \"\", b.axis ?? \"\")\n || compareIds(a.identity, b.identity));\n\n // `Set<string>`, not the ids' own brand: `DependencyEdge.from`/`to` are plain\n // strings, and the reassignment below rebuilds the set from a caller's\n // argument.\n let selected = new Set<string>(features.map((feature) => feature.id));\n if (featureId !== undefined) {\n selected = new Set<string>([featureId]);\n let changed = true;\n while (changed) {\n changed = false;\n for (const edge of edges) {\n if (selected.has(edge.from) && !selected.has(edge.to)) {\n selected.add(edge.to);\n changed = true;\n }\n }\n }\n edges = edges.filter((edge) => selected.has(edge.from) && selected.has(edge.to));\n }\n\n const selectedFeatures = features.filter((feature) => selected.has(feature.id));\n const ids = selectedFeatures.map((feature) => feature.id);\n const nodes = selectedFeatures.map((feature): DependencyNode => ({\n id: feature.id,\n dirName: feature.dirName,\n services: [...feature.services],\n dependsOn: edges.filter((edge) => edge.from === feature.id).map((edge) => edge.to).sort(compareIds),\n }));\n\n return {\n nodes,\n edges,\n conflicts: conflicts.filter((conflict) => conflict.features.some((id) => selected.has(id))),\n order: dependencyFirstOrder(ids, edges),\n cycles: stronglyConnected(ids, edges),\n };\n}\n"]}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * What one feature ADDS and what it NEEDS, read out of its own delta.
3
+ *
4
+ * The whole graph is built from these five lists and nothing else, which is why
5
+ * they are computed once per feature and indexed rather than re-derived per
6
+ * edge: a feature is a node here only because some other feature's requirement
7
+ * or operation resolves into it, and answering that per pair would be a fleet
8
+ * walk per pair.
9
+ *
10
+ * `reqKeys` returns BOTH spellings of a requirement's identity — the stable
11
+ * `Requirement-ID` and the exact heading — because a legacy requirement has only
12
+ * the second, and an edge that existed under one spelling and not the other
13
+ * would make the graph depend on when the corpus was migrated.
14
+ */
15
+ import { existsSync } from "node:fs";
16
+ import { FleetContext } from "../fleet-context.js";
17
+ import { serviceResolver } from "../c4/resolve/service.js";
18
+ import { operations } from "../openapi/doc.js";
19
+ import {} from "../repo/entries.js";
20
+ import { SPEC_AXES, featurePaths, featureSpecPaths } from "../repo/paths.js";
21
+ import { locateServicePaths } from "../repo/service-target.js";
22
+ import { enumeratedServiceIds, enumeratedServiceIndex } from "../repo/service-target.js";
23
+ export function reqKeys(requirement) {
24
+ return [
25
+ ...(requirement.id === undefined ? [] : [`id:${requirement.id}`]),
26
+ `name:${requirement.name}`,
27
+ ];
28
+ }
29
+ export function reqIndexKey(at) {
30
+ return `${at.service}\0${at.axis}\0${at.identity}`;
31
+ }
32
+ export function opIndexKey(at) {
33
+ return `${at.service}\0${at.operationId}`;
34
+ }
35
+ export function addOwner(index, key, at, feature) {
36
+ const owners = index.get(key) ?? { at, features: new Set() };
37
+ owners.features.add(feature);
38
+ index.set(key, owners);
39
+ }
40
+ export async function readFacts(docsDir, feature, context) {
41
+ const addedRequirements = [];
42
+ const changedRequirements = [];
43
+ const introducedOperations = [];
44
+ const requiredOperations = [];
45
+ /**
46
+ * The operationIds a service ALREADY has, per service.
47
+ *
48
+ * This is the whole difference between a dependency graph and a rumour mill.
49
+ * A feature's `openapi.yaml` is a COMPLETE document, not a patch — authors
50
+ * restate the living API around the slot they are changing — so every feature
51
+ * that so much as mentions `authorizePayment` used to be recorded as
52
+ * INTRODUCING it, and every other feature governing it as REQUIRING it. Two
53
+ * features quoting the same living operation therefore depended on each other
54
+ * in both directions: an invented cycle, an invented conflict, and an ordering
55
+ * for work that had none. `loam validate`'s coherence pass already subtracts
56
+ * the living contract this way (core/coherence/coherence.ts); this is the
57
+ * same subtraction, so the two cannot disagree about what "new" means.
58
+ *
59
+ * Cached per service inside the call and, through the FleetContext, across
60
+ * features: a fleet-wide graph asks about the same ten services N times.
61
+ */
62
+ const livingByService = new Map();
63
+ const living = async (service) => {
64
+ let ids = livingByService.get(service);
65
+ if (ids === undefined) {
66
+ ids = new Set((await operations((await locateServicePaths(docsDir, service, context)).openapi, context))
67
+ .filter((op) => !op.remove)
68
+ .map((op) => op.id));
69
+ livingByService.set(service, ids);
70
+ }
71
+ return ids;
72
+ };
73
+ for (const service of feature.services) {
74
+ const paths = featureSpecPaths(feature.dir, service);
75
+ const livingIds = await living(service);
76
+ for (const axis of SPEC_AXES) {
77
+ const path = paths[axis.key];
78
+ if (!existsSync(path))
79
+ continue;
80
+ const requirements = await context.readRequirements(path);
81
+ for (const requirement of requirements) {
82
+ const fact = { service, axis: axis.file, requirement };
83
+ if (requirement.kind === "ADDED")
84
+ addedRequirements.push(fact);
85
+ if (requirement.kind === "MODIFIED" || requirement.kind === "REMOVED") {
86
+ changedRequirements.push(fact);
87
+ }
88
+ for (const operationId of requirement.operations) {
89
+ // Governing an operation the service already provides is not a
90
+ // dependency on anybody: the contract it needs is already merged.
91
+ if (livingIds.has(operationId))
92
+ continue;
93
+ requiredOperations.push({ service, operationId });
94
+ }
95
+ }
96
+ }
97
+ if (existsSync(paths.openapi)) {
98
+ const featureOperations = await operations(paths.openapi, context);
99
+ for (const operation of featureOperations) {
100
+ if (livingIds.has(operation.id))
101
+ continue;
102
+ if (operation.remove)
103
+ requiredOperations.push({ service, operationId: operation.id });
104
+ else
105
+ introducedOperations.push({ service, operationId: operation.id });
106
+ }
107
+ }
108
+ }
109
+ // The C4 delta is the FOURTH place a feature names an operation, and it was
110
+ // the one the graph could not see: an edge carrying `metadata { op 'x' }` is
111
+ // a call this feature builds on `x`, which is exactly the "must land first"
112
+ // relation this module exists to compute. Validate already says so — it
113
+ // reports `c4-api.op-pending` when the op is defined by another feature in
114
+ // flight — so the graph disagreeing with the validator about the same pair of
115
+ // features was the graph being wrong, not quiet.
116
+ //
117
+ // Endpoints resolve through `serviceResolver` — the element's
118
+ // `metadata { service }` binding, title as fallback — the same join every
119
+ // other check uses, so renaming a box in a diagram cannot silently unhook it
120
+ // here either.
121
+ const deltaPath = featurePaths(feature.dir).delta;
122
+ if (existsSync(deltaPath)) {
123
+ const doc = await context.loadLikeC4(deltaPath);
124
+ // A delta that does not parse proves nothing about who depends on whom.
125
+ // `loam validate` owns that diagnosis (`delta.invalid`); inventing edges out
126
+ // of a half-read document would be worse than the silence.
127
+ if (doc.errors.length === 0) {
128
+ // One resolver for the whole delta: `serviceOf` rebuilds its id map on
129
+ // every call, and this loop asks once per edge. The enumerated fleet —
130
+ // `services/` plus this feature's own `specs/` directories, the same
131
+ // union `enumeratedTarget` probes below — rides into the resolver so a
132
+ // delta that models CONTAINERS keeps its edges attached: without it, an
133
+ // edge into `payment.api` resolves to a service called "api" that has
134
+ // never existed, the op lookup misses the living contract, and the graph
135
+ // invents a dependency (or orders two features that share nothing). An
136
+ // unenumerable services/ falls back to the feature's own names — every
137
+ // living probe would have found nothing there anyway.
138
+ const fleetIds = await enumeratedServiceIds(docsDir, context);
139
+ const svcOf = serviceResolver(doc.elements, new Set([...fleetIds, ...feature.services]));
140
+ // The resolver answers with document text and `living` builds a path, so
141
+ // the name crosses through the enumeration — coherence.ts grades its
142
+ // edges through the same bridge. A name no enumeration vouches for has
143
+ // no living contract anywhere: the op is required, exactly what the
144
+ // probe answered for an absent service.
145
+ const enumeratedTarget = enumeratedServiceIndex(docsDir, feature.services, context);
146
+ for (const rel of doc.relationships) {
147
+ if (rel.op === undefined)
148
+ continue;
149
+ const service = svcOf(rel.target);
150
+ const pathable = await enumeratedTarget(service);
151
+ if (pathable !== undefined && (await living(pathable)).has(rel.op))
152
+ continue;
153
+ requiredOperations.push({ service, operationId: rel.op });
154
+ }
155
+ }
156
+ }
157
+ return {
158
+ feature,
159
+ addedRequirements,
160
+ changedRequirements,
161
+ introducedOperations,
162
+ requiredOperations,
163
+ };
164
+ }
165
+ //# sourceMappingURL=facts.js.map