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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Does the diff apply to the thing it claims to change?
3
+ *
4
+ * A requirement delta is a diff against a living spec, and nothing used to check
5
+ * that it lands. Every failure mode here is silent today: MODIFIED of a
6
+ * requirement that does not exist is merged as a creation, ADDED of one that
7
+ * does exist REPLACES it (scenarios and all) while the author believes they are
8
+ * adding, and a heading that nearly matches the delta grammar parses as plain
9
+ * prose so archive merges nothing at all and says nothing about it.
10
+ *
11
+ * The last of those has a legal-looking cousin: a requirement under an ordinary
12
+ * prose heading (`## Behavior`, `## Error Handling`) is BASE too, and BASE never
13
+ * merges. Upstream OpenSpec deltas are written that way, so the answer is to name
14
+ * what will be lost rather than to start merging prose sections.
15
+ *
16
+ * These run inside the archive gate, because the merge is where the damage lands.
17
+ *
18
+ * TWO CORPORA, ONE ALGEBRA. A feature's delta documents live in two places —
19
+ * `specs/<svc>/{spec,arch.spec}.md` against the service tree, and
20
+ * `capabilities/<id>/spec.md` against the authored business tree — and every
21
+ * grade about a DOCUMENT and its living counterpart applies to both verbatim,
22
+ * with the capability id in `subject` where a service id would be. That is not
23
+ * a coincidence to exploit but the reason the business corpus was given deltas
24
+ * at all: a capability document is a requirements document, so the diff that
25
+ * changes one has exactly the same ways of not landing. A second implementation
26
+ * would be a second set of answers to `delta.added-duplicate`.
27
+ *
28
+ * The exception is the four CROSS-FEATURE warnings — `delta.added-conflict`,
29
+ * `delta.modified-conflict`, `delta.modified-pending`, `delta.removed-pending`
30
+ * — which ask what OTHER features in flight claim. `./claims.ts` indexes the
31
+ * service corpus only and states why; on the business axis those four are
32
+ * silent, which is a missing warning and never a wrong answer.
33
+ *
34
+ * This module is only the walk: which documents get read, in what order, and how
35
+ * each is handed a `DeltaScope`. The checks themselves are in `./document.ts`
36
+ * (a document against itself) and `./select.ts` (the delta against the living
37
+ * text). Their order is load-bearing and stays here, where it can be read in one
38
+ * screen.
39
+ */
40
+ import { existsSync } from "node:fs";
41
+ import { readFile } from "node:fs/promises";
42
+ import { relative } from "node:path";
43
+ import {} from "../fleet-context.js";
44
+ import { parseRequirements } from "../document/parse.js";
45
+ import {} from "../document/spec.js";
46
+ import { featureSpecPaths, SPEC_AXES } from "../repo/paths.js";
47
+ import { featureUseCasesDir, livingCapabilityPaths } from "../repo/authored/paths.js";
48
+ import { readCapabilityVocabulary } from "../capabilities/capabilities.js";
49
+ import { flowCoverage } from "../usecases/delta/claims.js";
50
+ import { enumeratedServiceIds, locateServicePaths } from "../repo/service-target.js";
51
+ import { featureSpecServices } from "../repo/repo.js";
52
+ import { capabilityDocIssues } from "../capabilities/delta/doc.js";
53
+ import { featureCapabilityDeltas } from "../capabilities/delta/tree.js";
54
+ import { removedRealizedIssues, uncoveredIssues, } from "../capabilities/delta/uncovered.js";
55
+ import {} from "../vocabulary/issue.js";
56
+ import { claimLookup } from "./claims.js";
57
+ import { deltaDocumentIssues, livingDocumentIssues } from "./document.js";
58
+ import { indexLiving } from "./scope.js";
59
+ import { selectionIssues } from "./select.js";
60
+ /**
61
+ * The business corpus's ONE axis. A capability document carries the promise and
62
+ * nothing else — there is no `arch.spec.md` beside it, because "how it is
63
+ * built" is precisely the altitude a capability requirement may not describe
64
+ * (`capability.requirement-service-scoped` is that rule). It has no `key`
65
+ * because it indexes no `ServicePaths`, which is why `DeltaScope.axis` is the
66
+ * narrower `DeltaAxis` and not `SpecAxis`.
67
+ */
68
+ const CAPABILITY_AXIS = { file: "spec.md" };
69
+ export async function deltaShapeIssues(docsDir, featureDir, featureId, context) {
70
+ const issues = [];
71
+ const services = await featureSpecServices(featureDir, context);
72
+ // One `existsSync` for a feature that carries no capability delta, which is
73
+ // every feature in a fleet that has not adopted the business axis.
74
+ const capabilities = context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);
75
+ // The flows this feature brings, and one `existsSync` for a feature that
76
+ // brings none — which is every feature in a fleet that has not adopted the
77
+ // axis. The directory, not the documents: what is needed here is whether to
78
+ // pay for the overlay at all, and the walk that lists them happens once,
79
+ // inside it.
80
+ const hasFlows = existsSync(featureUseCasesDir(featureDir));
81
+ // The early return must ask about EVERY corpus. Asking only about services
82
+ // meant a capability-only feature — a business change with no service touched
83
+ // yet, which is exactly what an analyst writes first — was graded by nothing
84
+ // and archived whatever its delta said; asking only about those two would do
85
+ // the same to a feature whose whole content is a cross-service flow, which is
86
+ // the shape an architect answers an analyst with.
87
+ if (services.length === 0 && capabilities.docs.length === 0 && !hasFlows)
88
+ return issues;
89
+ // What OTHER features in flight claim. Only built when a claim has to be
90
+ // checked — the common case never pays for the scan.
91
+ const reads = {
92
+ claims: claimLookup(docsDir, featureId, context),
93
+ ...(context === undefined ? {} : { context }),
94
+ };
95
+ // The two corpora's parsed requirements, kept as the walk produces them so
96
+ // the `Realizes:` join below can be taken without re-reading a byte. That
97
+ // join is the one question neither corpus can answer alone — which service
98
+ // requirement keeps which business promise — and this is the only place in
99
+ // loam where both sides are already open (`capabilities/delta/uncovered.ts`
100
+ // holds the rules; nothing here decides anything).
101
+ const serviceDeltas = [];
102
+ const capabilityDeltas = [];
103
+ // Both requirement-carrying files per service run the same checks — one code
104
+ // path parameterized by filename, the merge's own factoring. `where` names
105
+ // the arch file in messages so a finding cannot be chased into the wrong
106
+ // document; spec.md keeps its historical spelling.
107
+ for (const service of services) {
108
+ for (const axis of SPEC_AXES) {
109
+ const specPath = featureSpecPaths(featureDir, service)[axis.key];
110
+ if (!existsSync(specPath))
111
+ continue;
112
+ const scope = {
113
+ kind: "service",
114
+ subject: service,
115
+ axis,
116
+ featureId,
117
+ docsDir,
118
+ specPath,
119
+ where: axis.key === "spec" ? service : `${service} (arch.spec.md)`,
120
+ // How the axis's living document is named in messages — spec.md keeps
121
+ // the historical "living spec", the arch axis says which file it means.
122
+ livingDoc: axis.key === "spec" ? "living spec" : "living arch.spec.md",
123
+ };
124
+ const graded = await gradeDelta(scope, (await locateServicePaths(docsDir, service, context))[axis.key], reads);
125
+ issues.push(...graded.issues);
126
+ serviceDeltas.push({ service, file: axis.file, reqs: graded.reqs });
127
+ }
128
+ }
129
+ for (const doc of capabilities.docs) {
130
+ const scope = {
131
+ kind: "capability",
132
+ subject: doc.id,
133
+ axis: CAPABILITY_AXIS,
134
+ featureId,
135
+ docsDir,
136
+ specPath: doc.spec,
137
+ // `capability <id>`, not `capabilities/<id>` — the second reads as the
138
+ // LIVING directory, and every message this labels is about the FEATURE's
139
+ // delta of it. Sending an author to edit the wrong one of two files with
140
+ // the same name is the whole reason `where` and `livingDoc` are separate.
141
+ where: `capability ${doc.id}`,
142
+ livingDoc: `living capabilities/${doc.id}/spec.md`,
143
+ };
144
+ // Absent is a real and ordinary answer on this axis: the first feature to
145
+ // mention a capability is what creates its living document, so the whole
146
+ // delta grades against `[]` and every requirement in it is legitimately an
147
+ // addition.
148
+ const graded = await gradeDelta(scope, livingCapabilityPaths(docsDir, doc.id).spec, reads);
149
+ issues.push(...graded.issues);
150
+ // The document's own three rules, on the requirements this delta would
151
+ // MERGE — before the merge, where the author can still fix them. See
152
+ // `capabilities/delta/doc.ts` for why grading only the living copy leaves a
153
+ // hole straight through the altitude rule.
154
+ issues.push(...capabilityDocIssues(graded.reqs, {
155
+ where: relative(docsDir, doc.spec).split(/[\\/]/).join("/"),
156
+ subject: doc.id,
157
+ }));
158
+ capabilityDeltas.push({ id: doc.id, reqs: graded.reqs, living: graded.living.all });
159
+ }
160
+ // The other carrier of a promise, read once and only when this feature
161
+ // actually brings a flow: the `dynamic view`s under `features/<FEAT>/usecases/`,
162
+ // parsed over the map this feature's own merge would leave behind
163
+ // (`core/usecases/delta/overlay.ts`).
164
+ // A feature with no `usecases/` directory is a REAL empty answer, not an
165
+ // ungraded one: there is no flow, so "no flow keeps this" is true and the
166
+ // message may say it.
167
+ const kept = hasFlows
168
+ ? await flowCoverage({
169
+ docsDir,
170
+ featureDir,
171
+ featureId,
172
+ capabilities: capabilityDeltas,
173
+ // Read HERE, through the invocation's index, because this module holds
174
+ // it and `core/usecases/` does not — every other read in this walk does
175
+ // the same, and `core/repo/service-target.ts` prices a fleet
176
+ // enumeration per call at ~13 ms on a 120-service fleet.
177
+ vocabulary: context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir),
178
+ known: new Set(await enumeratedServiceIds(docsDir, context)),
179
+ // A function rather than the context itself: one memoised LikeC4 read
180
+ // does not justify an edge from `core/usecases/` to `fleet-context`, and
181
+ // the edge would push that package up a DAG level for nothing.
182
+ ...(context === undefined ? {} : { load: (path) => context.loadLikeC4(path) }),
183
+ })
184
+ : { kind: "read", kept: [] };
185
+ if (kept.kind === "unreadable") {
186
+ // ERROR, and the archive gate's own `NEVER_OVERRIDABLE` keeps `--approve`
187
+ // off it. This is the refusal that stops a flow being copied into
188
+ // `architecture/` and the NEXT reader's `loam validate --all` carrying the
189
+ // failure — including the case the overlay exists for, a hop naming an
190
+ // element the merge does not land. Mechanical, so it is not loam's judgement
191
+ // to override.
192
+ issues.push({
193
+ severity: "error",
194
+ code: "usecase.flow-invalid",
195
+ subject: featureId,
196
+ // The opening sentence does NOT say whose file is at fault, and that is
197
+ // deliberate: the corpus this reads is the feature's flows PLUS the living
198
+ // `architecture/` documents they are drawn over, so the failing path can
199
+ // be a living one — a landscape that no longer parses, a use case somebody
200
+ // else wrote. Every message carries its own path, so a reader can place
201
+ // it; an opening line that blamed `usecases/` would send them to the wrong
202
+ // file first.
203
+ message: `this feature's flows could not be graded against the map its own merge would leave behind, so the archive ` +
204
+ `would land an architecture/ nothing has checked. The corpus is the feature's usecases/ AND the living ` +
205
+ `architecture/ documents they are drawn over, so the fault may be in either — each message names its file. ` +
206
+ `${kept.errors.length} problem(s): ${kept.errors.join("; ")}`,
207
+ });
208
+ }
209
+ // The `Realizes:` join, both directions, over documents this walk has already
210
+ // parsed. Skipped entirely when the feature carries no capability delta,
211
+ // which is every feature in a fleet that has not adopted the business axis —
212
+ // and the removal half asks for the living corpus only when something is
213
+ // actually retired, so even an adopting fleet pays the fleet-wide read on the
214
+ // features that earn it.
215
+ if (capabilityDeltas.length > 0) {
216
+ // The three arms travel as three, and flattening two of them to `[]` is
217
+ // the one thing this must not do: `capability.uncovered` says in words that
218
+ // no flow keeps the promise, so handing it an empty list for a corpus loam
219
+ // could not read or could not resolve makes it assert a negative it never
220
+ // evaluated — and gate on it. `claims.ts`'s header states the rule; this is
221
+ // where a caller could break it.
222
+ issues.push(...uncoveredIssues(capabilityDeltas, serviceDeltas, kept.kind === "read"
223
+ ? { graded: true, kept: kept.kept }
224
+ : { graded: false, why: kept.kind === "ungraded" ? kept.why : kept.errors.join("; ") }));
225
+ issues.push(...(await removedRealizedIssues({
226
+ capabilities: capabilityDeltas,
227
+ deltas: serviceDeltas,
228
+ living: () => livingRealizers(docsDir, context),
229
+ })));
230
+ }
231
+ return issues;
232
+ }
233
+ /**
234
+ * Every LIVING service requirements document in the fleet, for the one question
235
+ * that has to ask about services this feature never mentions: does anything out
236
+ * there still realize a capability requirement this feature retires?
237
+ *
238
+ * The same shape and the same cost `openapi.remove-op-consumed` pays through
239
+ * `coherence/lookups.ts` one axis over, and for the same reason — the merge
240
+ * deletes the promise while the `Realizes:` line stays, so the very next
241
+ * `validate --all` reports a breach on a repository whose author was never in
242
+ * this feature. Called behind a thunk (`removedRealizedIssues` invokes it only
243
+ * when something is retired), so a feature that removes nothing never runs it.
244
+ */
245
+ async function livingRealizers(docsDir, context) {
246
+ const out = [];
247
+ for (const service of await enumeratedServiceIds(docsDir, context)) {
248
+ const paths = await locateServicePaths(docsDir, service, context);
249
+ for (const axis of SPEC_AXES) {
250
+ const path = paths[axis.key];
251
+ if (!existsSync(path))
252
+ continue;
253
+ const reqs = context === undefined ? parseRequirements(await readFile(path, "utf8")) : await context.readRequirements(path);
254
+ out.push({ service, file: axis.file, reqs });
255
+ }
256
+ }
257
+ return out;
258
+ }
259
+ /**
260
+ * One delta document against one living document: the three passes, in the
261
+ * order that makes their messages readable.
262
+ *
263
+ * The order is load-bearing. `deltaDocumentIssues` settles what the delta says
264
+ * about itself, `livingDocumentIssues` settles whether the living text can be
265
+ * selected in at all, and only then does `selectionIssues` compare them — a pin
266
+ * the document pass already refused must not also be reported stale, which
267
+ * would send its author to `loam rebase` for a problem rebase does not fix.
268
+ *
269
+ * The parsed delta requirements come back with the issues because the capability
270
+ * corpus grades them a second way (`capabilityDocIssues`) and re-reading the
271
+ * file to do it would be a second parse of bytes already in hand — and, without
272
+ * a context, a second chance for the two passes to disagree about the document.
273
+ * The LIVING index rides back for the same reason once more: the removal
274
+ * direction of the `Realizes:` join has to resolve a REMOVED spelled by heading
275
+ * into the id it retires, and that id is a fact about the living document.
276
+ */
277
+ async function gradeDelta(scope, livingPath, reads) {
278
+ const { claims, context } = reads;
279
+ const raw = context === undefined ? await readFile(scope.specPath, "utf8") : await context.readText(scope.specPath);
280
+ const reqs = context === undefined ? parseRequirements(raw) : await context.readRequirements(scope.specPath);
281
+ const living = indexLiving(!existsSync(livingPath)
282
+ ? []
283
+ : context === undefined
284
+ ? parseRequirements(await readFile(livingPath, "utf8"))
285
+ : await context.readRequirements(livingPath));
286
+ return {
287
+ reqs,
288
+ living,
289
+ issues: [
290
+ ...deltaDocumentIssues(scope, raw, reqs),
291
+ ...livingDocumentIssues(scope, living),
292
+ ...(await selectionIssues(scope, reqs, living, claims)),
293
+ ],
294
+ };
295
+ }
296
+ //# sourceMappingURL=delta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta.js","sourceRoot":"","sources":["../../../src/core/delta/delta.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Does the diff apply to the thing it claims to change?\n *\n * A requirement delta is a diff against a living spec, and nothing used to check\n * that it lands. Every failure mode here is silent today: MODIFIED of a\n * requirement that does not exist is merged as a creation, ADDED of one that\n * does exist REPLACES it (scenarios and all) while the author believes they are\n * adding, and a heading that nearly matches the delta grammar parses as plain\n * prose so archive merges nothing at all and says nothing about it.\n *\n * The last of those has a legal-looking cousin: a requirement under an ordinary\n * prose heading (`## Behavior`, `## Error Handling`) is BASE too, and BASE never\n * merges. Upstream OpenSpec deltas are written that way, so the answer is to name\n * what will be lost rather than to start merging prose sections.\n *\n * These run inside the archive gate, because the merge is where the damage lands.\n *\n * TWO CORPORA, ONE ALGEBRA. A feature's delta documents live in two places —\n * `specs/<svc>/{spec,arch.spec}.md` against the service tree, and\n * `capabilities/<id>/spec.md` against the authored business tree — and every\n * grade about a DOCUMENT and its living counterpart applies to both verbatim,\n * with the capability id in `subject` where a service id would be. That is not\n * a coincidence to exploit but the reason the business corpus was given deltas\n * at all: a capability document is a requirements document, so the diff that\n * changes one has exactly the same ways of not landing. A second implementation\n * would be a second set of answers to `delta.added-duplicate`.\n *\n * The exception is the four CROSS-FEATURE warnings — `delta.added-conflict`,\n * `delta.modified-conflict`, `delta.modified-pending`, `delta.removed-pending`\n * — which ask what OTHER features in flight claim. `./claims.ts` indexes the\n * service corpus only and states why; on the business axis those four are\n * silent, which is a missing warning and never a wrong answer.\n *\n * This module is only the walk: which documents get read, in what order, and how\n * each is handed a `DeltaScope`. The checks themselves are in `./document.ts`\n * (a document against itself) and `./select.ts` (the delta against the living\n * text). Their order is load-bearing and stays here, where it can be read in one\n * screen.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport { type FleetContext } from \"../fleet-context.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport { type Requirement } from \"../document/spec.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../repo/paths.js\";\nimport { featureUseCasesDir, livingCapabilityPaths } from \"../repo/authored/paths.js\";\nimport { readCapabilityVocabulary } from \"../capabilities/capabilities.js\";\n\nimport { flowCoverage, type FlowPromises } from \"../usecases/delta/claims.js\";\nimport { enumeratedServiceIds, locateServicePaths } from \"../repo/service-target.js\";\nimport { featureSpecServices } from \"../repo/repo.js\";\nimport { capabilityDocIssues } from \"../capabilities/delta/doc.js\";\nimport { featureCapabilityDeltas } from \"../capabilities/delta/tree.js\";\nimport {\n removedRealizedIssues,\n uncoveredIssues,\n type CapabilityDeltaDoc,\n type RealizingDoc,\n} from \"../capabilities/delta/uncovered.js\";\nimport { type Issue } from \"../vocabulary/issue.js\";\nimport { claimLookup, type ClaimLookup } from \"./claims.js\";\nimport { deltaDocumentIssues, livingDocumentIssues } from \"./document.js\";\nimport { indexLiving, type DeltaScope, type LivingIndex } from \"./scope.js\";\nimport { selectionIssues } from \"./select.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * The business corpus's ONE axis. A capability document carries the promise and\n * nothing else — there is no `arch.spec.md` beside it, because \"how it is\n * built\" is precisely the altitude a capability requirement may not describe\n * (`capability.requirement-service-scoped` is that rule). It has no `key`\n * because it indexes no `ServicePaths`, which is why `DeltaScope.axis` is the\n * narrower `DeltaAxis` and not `SpecAxis`.\n */\nconst CAPABILITY_AXIS = { file: \"spec.md\" };\n\n/**\n * What every document in one feature's walk shares.\n *\n * A record rather than two more parameters because both values are about the\n * WALK and not about the document being graded — `gradeDelta` would otherwise\n * read as a four-argument function whose last two arguments never vary within\n * a run, which is how the next value gets added as a fifth.\n */\ninterface DeltaReads {\n /** What OTHER features in flight claim — lazy, so the common case never scans. */\n claims: ClaimLookup;\n /** The invocation's read index, when the caller threaded one. */\n context?: FleetContext;\n}\n\nexport async function deltaShapeIssues(\n docsDir: DocsDir,\n featureDir: FeatureDir,\n featureId: string,\n context?: FleetContext,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n const services = await featureSpecServices(featureDir, context);\n // One `existsSync` for a feature that carries no capability delta, which is\n // every feature in a fleet that has not adopted the business axis.\n const capabilities =\n context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);\n // The flows this feature brings, and one `existsSync` for a feature that\n // brings none — which is every feature in a fleet that has not adopted the\n // axis. The directory, not the documents: what is needed here is whether to\n // pay for the overlay at all, and the walk that lists them happens once,\n // inside it.\n const hasFlows = existsSync(featureUseCasesDir(featureDir));\n // The early return must ask about EVERY corpus. Asking only about services\n // meant a capability-only feature — a business change with no service touched\n // yet, which is exactly what an analyst writes first — was graded by nothing\n // and archived whatever its delta said; asking only about those two would do\n // the same to a feature whose whole content is a cross-service flow, which is\n // the shape an architect answers an analyst with.\n if (services.length === 0 && capabilities.docs.length === 0 && !hasFlows) return issues;\n\n // What OTHER features in flight claim. Only built when a claim has to be\n // checked — the common case never pays for the scan.\n const reads: DeltaReads = {\n claims: claimLookup(docsDir, featureId, context),\n ...(context === undefined ? {} : { context }),\n };\n\n // The two corpora's parsed requirements, kept as the walk produces them so\n // the `Realizes:` join below can be taken without re-reading a byte. That\n // join is the one question neither corpus can answer alone — which service\n // requirement keeps which business promise — and this is the only place in\n // loam where both sides are already open (`capabilities/delta/uncovered.ts`\n // holds the rules; nothing here decides anything).\n const serviceDeltas: RealizingDoc[] = [];\n const capabilityDeltas: CapabilityDeltaDoc[] = [];\n\n // Both requirement-carrying files per service run the same checks — one code\n // path parameterized by filename, the merge's own factoring. `where` names\n // the arch file in messages so a finding cannot be chased into the wrong\n // document; spec.md keeps its historical spelling.\n for (const service of services) {\n for (const axis of SPEC_AXES) {\n const specPath = featureSpecPaths(featureDir, service)[axis.key];\n if (!existsSync(specPath)) continue;\n const scope: DeltaScope = {\n kind: \"service\",\n subject: service,\n axis,\n featureId,\n docsDir,\n specPath,\n where: axis.key === \"spec\" ? service : `${service} (arch.spec.md)`,\n // How the axis's living document is named in messages — spec.md keeps\n // the historical \"living spec\", the arch axis says which file it means.\n livingDoc: axis.key === \"spec\" ? \"living spec\" : \"living arch.spec.md\",\n };\n const graded = await gradeDelta(scope, (await locateServicePaths(docsDir, service, context))[axis.key], reads);\n issues.push(...graded.issues);\n serviceDeltas.push({ service, file: axis.file, reqs: graded.reqs });\n }\n }\n\n for (const doc of capabilities.docs) {\n const scope: DeltaScope = {\n kind: \"capability\",\n subject: doc.id,\n axis: CAPABILITY_AXIS,\n featureId,\n docsDir,\n specPath: doc.spec,\n // `capability <id>`, not `capabilities/<id>` — the second reads as the\n // LIVING directory, and every message this labels is about the FEATURE's\n // delta of it. Sending an author to edit the wrong one of two files with\n // the same name is the whole reason `where` and `livingDoc` are separate.\n where: `capability ${doc.id}`,\n livingDoc: `living capabilities/${doc.id}/spec.md`,\n };\n // Absent is a real and ordinary answer on this axis: the first feature to\n // mention a capability is what creates its living document, so the whole\n // delta grades against `[]` and every requirement in it is legitimately an\n // addition.\n const graded = await gradeDelta(scope, livingCapabilityPaths(docsDir, doc.id).spec, reads);\n issues.push(...graded.issues);\n // The document's own three rules, on the requirements this delta would\n // MERGE — before the merge, where the author can still fix them. See\n // `capabilities/delta/doc.ts` for why grading only the living copy leaves a\n // hole straight through the altitude rule.\n issues.push(\n ...capabilityDocIssues(graded.reqs, {\n where: relative(docsDir, doc.spec).split(/[\\\\/]/).join(\"/\"),\n subject: doc.id,\n }),\n );\n capabilityDeltas.push({ id: doc.id, reqs: graded.reqs, living: graded.living.all });\n }\n\n // The other carrier of a promise, read once and only when this feature\n // actually brings a flow: the `dynamic view`s under `features/<FEAT>/usecases/`,\n // parsed over the map this feature's own merge would leave behind\n // (`core/usecases/delta/overlay.ts`).\n // A feature with no `usecases/` directory is a REAL empty answer, not an\n // ungraded one: there is no flow, so \"no flow keeps this\" is true and the\n // message may say it.\n const kept: FlowPromises = hasFlows\n ? await flowCoverage({\n docsDir,\n featureDir,\n featureId,\n capabilities: capabilityDeltas,\n // Read HERE, through the invocation's index, because this module holds\n // it and `core/usecases/` does not — every other read in this walk does\n // the same, and `core/repo/service-target.ts` prices a fleet\n // enumeration per call at ~13 ms on a 120-service fleet.\n vocabulary: context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir),\n known: new Set(await enumeratedServiceIds(docsDir, context)),\n // A function rather than the context itself: one memoised LikeC4 read\n // does not justify an edge from `core/usecases/` to `fleet-context`, and\n // the edge would push that package up a DAG level for nothing.\n ...(context === undefined ? {} : { load: (path: string) => context.loadLikeC4(path) }),\n })\n : { kind: \"read\", kept: [] };\n if (kept.kind === \"unreadable\") {\n // ERROR, and the archive gate's own `NEVER_OVERRIDABLE` keeps `--approve`\n // off it. This is the refusal that stops a flow being copied into\n // `architecture/` and the NEXT reader's `loam validate --all` carrying the\n // failure — including the case the overlay exists for, a hop naming an\n // element the merge does not land. Mechanical, so it is not loam's judgement\n // to override.\n issues.push({\n severity: \"error\",\n code: \"usecase.flow-invalid\",\n subject: featureId,\n // The opening sentence does NOT say whose file is at fault, and that is\n // deliberate: the corpus this reads is the feature's flows PLUS the living\n // `architecture/` documents they are drawn over, so the failing path can\n // be a living one — a landscape that no longer parses, a use case somebody\n // else wrote. Every message carries its own path, so a reader can place\n // it; an opening line that blamed `usecases/` would send them to the wrong\n // file first.\n message:\n `this feature's flows could not be graded against the map its own merge would leave behind, so the archive ` +\n `would land an architecture/ nothing has checked. The corpus is the feature's usecases/ AND the living ` +\n `architecture/ documents they are drawn over, so the fault may be in either — each message names its file. ` +\n `${kept.errors.length} problem(s): ${kept.errors.join(\"; \")}`,\n });\n }\n\n // The `Realizes:` join, both directions, over documents this walk has already\n // parsed. Skipped entirely when the feature carries no capability delta,\n // which is every feature in a fleet that has not adopted the business axis —\n // and the removal half asks for the living corpus only when something is\n // actually retired, so even an adopting fleet pays the fleet-wide read on the\n // features that earn it.\n if (capabilityDeltas.length > 0) {\n // The three arms travel as three, and flattening two of them to `[]` is\n // the one thing this must not do: `capability.uncovered` says in words that\n // no flow keeps the promise, so handing it an empty list for a corpus loam\n // could not read or could not resolve makes it assert a negative it never\n // evaluated — and gate on it. `claims.ts`'s header states the rule; this is\n // where a caller could break it.\n issues.push(\n ...uncoveredIssues(\n capabilityDeltas,\n serviceDeltas,\n kept.kind === \"read\"\n ? { graded: true, kept: kept.kept }\n : { graded: false, why: kept.kind === \"ungraded\" ? kept.why : kept.errors.join(\"; \") },\n ),\n );\n issues.push(\n ...(await removedRealizedIssues({\n capabilities: capabilityDeltas,\n deltas: serviceDeltas,\n living: () => livingRealizers(docsDir, context),\n })),\n );\n }\n\n return issues;\n}\n\n/**\n * Every LIVING service requirements document in the fleet, for the one question\n * that has to ask about services this feature never mentions: does anything out\n * there still realize a capability requirement this feature retires?\n *\n * The same shape and the same cost `openapi.remove-op-consumed` pays through\n * `coherence/lookups.ts` one axis over, and for the same reason — the merge\n * deletes the promise while the `Realizes:` line stays, so the very next\n * `validate --all` reports a breach on a repository whose author was never in\n * this feature. Called behind a thunk (`removedRealizedIssues` invokes it only\n * when something is retired), so a feature that removes nothing never runs it.\n */\nasync function livingRealizers(docsDir: DocsDir, context?: FleetContext): Promise<RealizingDoc[]> {\n const out: RealizingDoc[] = [];\n for (const service of await enumeratedServiceIds(docsDir, context)) {\n const paths = await locateServicePaths(docsDir, service, context);\n for (const axis of SPEC_AXES) {\n const path = paths[axis.key];\n if (!existsSync(path)) continue;\n const reqs =\n context === undefined ? parseRequirements(await readFile(path, \"utf8\")) : await context.readRequirements(path);\n out.push({ service, file: axis.file, reqs });\n }\n }\n return out;\n}\n\n/**\n * One delta document against one living document: the three passes, in the\n * order that makes their messages readable.\n *\n * The order is load-bearing. `deltaDocumentIssues` settles what the delta says\n * about itself, `livingDocumentIssues` settles whether the living text can be\n * selected in at all, and only then does `selectionIssues` compare them — a pin\n * the document pass already refused must not also be reported stale, which\n * would send its author to `loam rebase` for a problem rebase does not fix.\n *\n * The parsed delta requirements come back with the issues because the capability\n * corpus grades them a second way (`capabilityDocIssues`) and re-reading the\n * file to do it would be a second parse of bytes already in hand — and, without\n * a context, a second chance for the two passes to disagree about the document.\n * The LIVING index rides back for the same reason once more: the removal\n * direction of the `Realizes:` join has to resolve a REMOVED spelled by heading\n * into the id it retires, and that id is a fact about the living document.\n */\nasync function gradeDelta(\n scope: DeltaScope,\n livingPath: string,\n reads: DeltaReads,\n): Promise<{ issues: Issue[]; reqs: Requirement[]; living: LivingIndex }> {\n const { claims, context } = reads;\n const raw = context === undefined ? await readFile(scope.specPath, \"utf8\") : await context.readText(scope.specPath);\n const reqs = context === undefined ? parseRequirements(raw) : await context.readRequirements(scope.specPath);\n const living = indexLiving(\n !existsSync(livingPath)\n ? []\n : context === undefined\n ? parseRequirements(await readFile(livingPath, \"utf8\"))\n : await context.readRequirements(livingPath),\n );\n return {\n reqs,\n living,\n issues: [\n ...deltaDocumentIssues(scope, raw, reqs),\n ...livingDocumentIssues(scope, living),\n ...(await selectionIssues(scope, reqs, living, claims)),\n ],\n };\n}\n"]}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * What a requirements document says about ITSELF — graded before anything is
3
+ * compared against another document.
4
+ *
5
+ * Both the delta and the living spec come through here, because the failures are
6
+ * the same class: a heading that nearly matches the delta grammar, an identity
7
+ * declared twice or shared by two requirements, a `Based-On:` nobody can
8
+ * evaluate. Settling them first is what lets `./select.ts` assume the two
9
+ * documents are at least internally answerable — a pin the document pass already
10
+ * refused must not also be reported stale, which would send its author to
11
+ * `loam rebase` for a problem rebase does not fix.
12
+ */
13
+ import { relative } from "node:path";
14
+ import {} from "../vocabulary/issue.js";
15
+ import { sectionHeadings } from "../document/parse.js";
16
+ import { basedOnDeclarations, KIND_RE, REQUIREMENT_DIGEST_LENGTH, REQUIREMENT_DIGEST_RE, requirementIdProblems, } from "../document/spec.js";
17
+ import {} from "./scope.js";
18
+ /**
19
+ * `## <word> Requirement(s)` — the shape an author reaches for. Anything matching
20
+ * this but not the real grammar is a near miss: `## ADDED Requirement` (singular),
21
+ * `## NEW Requirements`, `## ADDED Requirements:`. A bare `## Requirements` is not
22
+ * a near miss — quoting the living state inside a delta is legal.
23
+ */
24
+ const NEAR_SECTION_RE = /^##\s+[A-Za-z]+\s+Requirements?\s*:?\s*$/i;
25
+ /**
26
+ * OpenSpec's fourth delta operation. NEAR_SECTION_RE already catches it, but the
27
+ * generic advice would mislead: a RENAMED body is FROM/TO backtick bullets, not
28
+ * `### Requirement:` headings, so it parses to zero requirements and even the
29
+ * whole-file check below has nothing to count — the heading is the only place to
30
+ * stop it. Rename semantics stay unimplemented (the corpus never uses them); the
31
+ * error says what to write instead.
32
+ */
33
+ const RENAMED_SECTION_RE = /^##\s+RENAMED\s+Requirements?\s*:?\s*$/i;
34
+ // The one non-delta heading whose requirements are SUPPOSED not to merge — a
35
+ // delta quoting the living state for context — is `## Requirements`, matched by
36
+ // the shared isRequirementsHeading (spec.ts): the same definition that fixes
37
+ // archive's rewrite boundary, so "legal to quote" and "where the merge writes"
38
+ // cannot drift apart. Every other heading — `## Behavior`, `## Error Handling`,
39
+ // `## Notes` — leaves its requirements as BASE, which the merge skips, so the
40
+ // author wrote a change that will never land.
41
+ /** Everything wrong with the DELTA document on its own terms. */
42
+ export function deltaDocumentIssues(scope, raw, reqs) {
43
+ const { docsDir, subject, specPath, where } = scope;
44
+ const issues = [];
45
+ for (const heading of sectionHeadings(raw)) {
46
+ if (KIND_RE.test(heading.text) || !NEAR_SECTION_RE.test(heading.text))
47
+ continue;
48
+ issues.push({
49
+ severity: "error",
50
+ code: "delta.unknown-section",
51
+ subject,
52
+ message: RENAMED_SECTION_RE.test(heading.text)
53
+ ? `${where}: '${heading.text}' (line ${heading.line}) is OpenSpec rename syntax, which loam does not merge — carry one stable Requirement-ID through a MODIFIED requirement, or express a legacy rename as a REMOVED requirement plus an ADDED one; otherwise the rename happens to nothing`
54
+ : `${where}: '${heading.text}' (line ${heading.line}) is not a delta section — use '## ADDED Requirements', '## MODIFIED Requirements' or '## REMOVED Requirements', or everything under it merges as nothing`,
55
+ });
56
+ }
57
+ for (const problem of requirementIdProblems(reqs)) {
58
+ if (problem.kind === "invalid") {
59
+ issues.push({
60
+ severity: "error",
61
+ code: "delta.requirement-id-invalid",
62
+ subject,
63
+ message: `${where}: requirement '${problem.requirement}' has invalid Requirement-ID '${problem.value}' — use 1-128 characters matching [A-Za-z][A-Za-z0-9._-]*`,
64
+ });
65
+ }
66
+ else if (problem.kind === "repeated") {
67
+ issues.push({
68
+ severity: "error",
69
+ code: "delta.requirement-id-repeated",
70
+ subject,
71
+ message: `${where}: requirement '${problem.requirement}' declares Requirement-ID ${problem.values.length} times — identity must be declared exactly once`,
72
+ });
73
+ }
74
+ else {
75
+ issues.push({
76
+ severity: "error",
77
+ code: "delta.requirement-id-duplicate",
78
+ subject,
79
+ message: `${where}: Requirement-ID '${problem.id}' is shared by ${problem.requirements.map((name) => `'${name}'`).join(", ")} — one ID may identify only one requirement`,
80
+ });
81
+ }
82
+ }
83
+ // The `Based-On:` pin as a DOCUMENT question, settled before anything is
84
+ // compared against the living spec: a value that is not a digest, or two
85
+ // of them on one requirement, is a pin nobody can evaluate — and a pin
86
+ // that silently protects nothing is worse than no pin at all, because its
87
+ // author believes they are covered. ADDED is refused for the same reason
88
+ // rather than ignored: a requirement with no living version cannot have
89
+ // been written against one, so the line is a misunderstanding today and a
90
+ // digest-shaped lie on the day someone turns it into a MODIFIED.
91
+ for (const r of reqs) {
92
+ const declared = basedOnDeclarations(r);
93
+ if (declared.length > 1) {
94
+ issues.push({
95
+ severity: "error",
96
+ code: "delta.baseline-invalid",
97
+ subject,
98
+ message: `${where}: requirement '${r.name}' declares Based-On ${declared.length} times — a delta is written against exactly one living version`,
99
+ });
100
+ continue;
101
+ }
102
+ const pin = declared[0];
103
+ if (pin === undefined)
104
+ continue;
105
+ if (!REQUIREMENT_DIGEST_RE.test(pin)) {
106
+ issues.push({
107
+ severity: "error",
108
+ code: "delta.baseline-invalid",
109
+ subject,
110
+ message: `${where}: requirement '${r.name}' has invalid Based-On '${pin}' — expected ${REQUIREMENT_DIGEST_LENGTH} lowercase hex characters, as \`loam rebase\` writes them`,
111
+ });
112
+ }
113
+ else if (r.kind === "ADDED") {
114
+ issues.push({
115
+ severity: "error",
116
+ code: "delta.baseline-invalid",
117
+ subject,
118
+ message: `${where}: ADDED requirement '${r.name}' carries Based-On '${pin}', but an added requirement has no living version to be based on — drop the line, or make it MODIFIED`,
119
+ });
120
+ }
121
+ }
122
+ // The whole-file version of the near-miss check, and the one that cannot be
123
+ // dodged: NEAR_SECTION_RE only recognizes single-word misses (`## NEW
124
+ // Requirements`), so `## NEWLY ADDED Requirements` — or requirements under a
125
+ // prose heading, or under no heading at all — still parses everything as BASE
126
+ // and archive merges nothing, silently. Same failure class as the BOM incident
127
+ // (see spec.ts). A file that MIXES delta sections with quoted BASE requirements
128
+ // is legal authoring; a file with requirements and no delta kind anywhere is a
129
+ // delta that would do nothing, which cannot be what its author meant.
130
+ if (reqs.length > 0 && reqs.every((r) => r.kind === "BASE")) {
131
+ const rel = relative(docsDir, specPath).split(/[\\/]/).join("/");
132
+ issues.push({
133
+ severity: "error",
134
+ code: "delta.no-delta-sections",
135
+ subject,
136
+ message: `${subject}: ${rel} contains ${reqs.length} requirement(s) but no '## ADDED|MODIFIED|REMOVED Requirements' section — this delta would merge nothing`,
137
+ });
138
+ }
139
+ return issues;
140
+ }
141
+ /** Everything wrong with the LIVING document that stops a delta selecting in it. */
142
+ export function livingDocumentIssues(scope, living) {
143
+ const { subject, where, livingDoc } = scope;
144
+ const issues = [];
145
+ for (const problem of requirementIdProblems(living.all)) {
146
+ issues.push({
147
+ severity: "error",
148
+ code: "delta.living-requirement-id-invalid",
149
+ subject,
150
+ message: problem.kind === "invalid"
151
+ ? `${where}: the ${livingDoc} requirement '${problem.requirement}' has invalid Requirement-ID '${problem.value}', so this delta cannot select it safely`
152
+ : problem.kind === "repeated"
153
+ ? `${where}: the ${livingDoc} requirement '${problem.requirement}' declares Requirement-ID more than once, so this delta cannot select it safely`
154
+ : `${where}: the ${livingDoc} shares Requirement-ID '${problem.id}' across ${problem.requirements.map((name) => `'${name}'`).join(", ")}, so this delta cannot select one safely`,
155
+ });
156
+ }
157
+ // Two living requirements under one heading make the delta algebra
158
+ // disagree with itself on the SAME input: MODIFIED replaces the first
159
+ // match (findIndex) while REMOVED deletes every match (filter). So one
160
+ // delta edits one of the twins and leaves the other, and the next one
161
+ // deletes both — and neither outcome is what an author reading the
162
+ // living document could have predicted. Gating rather than advisory,
163
+ // because there is no reading of the delta that makes the merge correct;
164
+ // the fix is in the living document, and it is one rename.
165
+ for (const [name, twins] of living.byName) {
166
+ if (twins.length < 2)
167
+ continue;
168
+ issues.push({
169
+ severity: "error",
170
+ code: "delta.living-duplicate-requirement",
171
+ subject,
172
+ message: `${where}: the ${livingDoc} declares ${twins.length} requirements named '${name}' — MODIFIED would rewrite only the first and REMOVED would delete both, so no delta applies to it predictably. Give them distinct names (or distinct Requirement-IDs and headings) first.`,
173
+ });
174
+ }
175
+ return issues;
176
+ }
177
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../../src/core/delta/document.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a requirements document says about ITSELF — graded before anything is\n * compared against another document.\n *\n * Both the delta and the living spec come through here, because the failures are\n * the same class: a heading that nearly matches the delta grammar, an identity\n * declared twice or shared by two requirements, a `Based-On:` nobody can\n * evaluate. Settling them first is what lets `./select.ts` assume the two\n * documents are at least internally answerable — a pin the document pass already\n * refused must not also be reported stale, which would send its author to\n * `loam rebase` for a problem rebase does not fix.\n */\nimport { relative } from \"node:path\";\nimport { type Issue } from \"../vocabulary/issue.js\";\nimport { sectionHeadings } from \"../document/parse.js\";\nimport {\n basedOnDeclarations,\n KIND_RE,\n REQUIREMENT_DIGEST_LENGTH,\n REQUIREMENT_DIGEST_RE,\n requirementIdProblems,\n type Requirement,\n} from \"../document/spec.js\";\nimport { type DeltaScope, type LivingIndex } from \"./scope.js\";\n\n/**\n * `## <word> Requirement(s)` — the shape an author reaches for. Anything matching\n * this but not the real grammar is a near miss: `## ADDED Requirement` (singular),\n * `## NEW Requirements`, `## ADDED Requirements:`. A bare `## Requirements` is not\n * a near miss — quoting the living state inside a delta is legal.\n */\nconst NEAR_SECTION_RE = /^##\\s+[A-Za-z]+\\s+Requirements?\\s*:?\\s*$/i;\n\n/**\n * OpenSpec's fourth delta operation. NEAR_SECTION_RE already catches it, but the\n * generic advice would mislead: a RENAMED body is FROM/TO backtick bullets, not\n * `### Requirement:` headings, so it parses to zero requirements and even the\n * whole-file check below has nothing to count — the heading is the only place to\n * stop it. Rename semantics stay unimplemented (the corpus never uses them); the\n * error says what to write instead.\n */\nconst RENAMED_SECTION_RE = /^##\\s+RENAMED\\s+Requirements?\\s*:?\\s*$/i;\n\n// The one non-delta heading whose requirements are SUPPOSED not to merge — a\n// delta quoting the living state for context — is `## Requirements`, matched by\n// the shared isRequirementsHeading (spec.ts): the same definition that fixes\n// archive's rewrite boundary, so \"legal to quote\" and \"where the merge writes\"\n// cannot drift apart. Every other heading — `## Behavior`, `## Error Handling`,\n// `## Notes` — leaves its requirements as BASE, which the merge skips, so the\n// author wrote a change that will never land.\n\n/** Everything wrong with the DELTA document on its own terms. */\nexport function deltaDocumentIssues(scope: DeltaScope, raw: string, reqs: Requirement[]): Issue[] {\n const { docsDir, subject, specPath, where } = scope;\n const issues: Issue[] = [];\n for (const heading of sectionHeadings(raw)) {\n if (KIND_RE.test(heading.text) || !NEAR_SECTION_RE.test(heading.text)) continue;\n issues.push({\n severity: \"error\",\n code: \"delta.unknown-section\",\n subject,\n message: RENAMED_SECTION_RE.test(heading.text)\n ? `${where}: '${heading.text}' (line ${heading.line}) is OpenSpec rename syntax, which loam does not merge — carry one stable Requirement-ID through a MODIFIED requirement, or express a legacy rename as a REMOVED requirement plus an ADDED one; otherwise the rename happens to nothing`\n : `${where}: '${heading.text}' (line ${heading.line}) is not a delta section — use '## ADDED Requirements', '## MODIFIED Requirements' or '## REMOVED Requirements', or everything under it merges as nothing`,\n });\n }\n\n for (const problem of requirementIdProblems(reqs)) {\n if (problem.kind === \"invalid\") {\n issues.push({\n severity: \"error\",\n code: \"delta.requirement-id-invalid\",\n subject,\n message: `${where}: requirement '${problem.requirement}' has invalid Requirement-ID '${problem.value}' — use 1-128 characters matching [A-Za-z][A-Za-z0-9._-]*`,\n });\n } else if (problem.kind === \"repeated\") {\n issues.push({\n severity: \"error\",\n code: \"delta.requirement-id-repeated\",\n subject,\n message: `${where}: requirement '${problem.requirement}' declares Requirement-ID ${problem.values.length} times — identity must be declared exactly once`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"delta.requirement-id-duplicate\",\n subject,\n message: `${where}: Requirement-ID '${problem.id}' is shared by ${problem.requirements.map((name) => `'${name}'`).join(\", \")} — one ID may identify only one requirement`,\n });\n }\n }\n\n // The `Based-On:` pin as a DOCUMENT question, settled before anything is\n // compared against the living spec: a value that is not a digest, or two\n // of them on one requirement, is a pin nobody can evaluate — and a pin\n // that silently protects nothing is worse than no pin at all, because its\n // author believes they are covered. ADDED is refused for the same reason\n // rather than ignored: a requirement with no living version cannot have\n // been written against one, so the line is a misunderstanding today and a\n // digest-shaped lie on the day someone turns it into a MODIFIED.\n for (const r of reqs) {\n const declared = basedOnDeclarations(r);\n if (declared.length > 1) {\n issues.push({\n severity: \"error\",\n code: \"delta.baseline-invalid\",\n subject,\n message: `${where}: requirement '${r.name}' declares Based-On ${declared.length} times — a delta is written against exactly one living version`,\n });\n continue;\n }\n const pin = declared[0];\n if (pin === undefined) continue;\n if (!REQUIREMENT_DIGEST_RE.test(pin)) {\n issues.push({\n severity: \"error\",\n code: \"delta.baseline-invalid\",\n subject,\n message: `${where}: requirement '${r.name}' has invalid Based-On '${pin}' — expected ${REQUIREMENT_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n } else if (r.kind === \"ADDED\") {\n issues.push({\n severity: \"error\",\n code: \"delta.baseline-invalid\",\n subject,\n message: `${where}: ADDED requirement '${r.name}' carries Based-On '${pin}', but an added requirement has no living version to be based on — drop the line, or make it MODIFIED`,\n });\n }\n }\n\n // The whole-file version of the near-miss check, and the one that cannot be\n // dodged: NEAR_SECTION_RE only recognizes single-word misses (`## NEW\n // Requirements`), so `## NEWLY ADDED Requirements` — or requirements under a\n // prose heading, or under no heading at all — still parses everything as BASE\n // and archive merges nothing, silently. Same failure class as the BOM incident\n // (see spec.ts). A file that MIXES delta sections with quoted BASE requirements\n // is legal authoring; a file with requirements and no delta kind anywhere is a\n // delta that would do nothing, which cannot be what its author meant.\n if (reqs.length > 0 && reqs.every((r) => r.kind === \"BASE\")) {\n const rel = relative(docsDir, specPath).split(/[\\\\/]/).join(\"/\");\n issues.push({\n severity: \"error\",\n code: \"delta.no-delta-sections\",\n subject,\n message: `${subject}: ${rel} contains ${reqs.length} requirement(s) but no '## ADDED|MODIFIED|REMOVED Requirements' section — this delta would merge nothing`,\n });\n }\n return issues;\n}\n\n/** Everything wrong with the LIVING document that stops a delta selecting in it. */\nexport function livingDocumentIssues(scope: DeltaScope, living: LivingIndex): Issue[] {\n const { subject, where, livingDoc } = scope;\n const issues: Issue[] = [];\n for (const problem of requirementIdProblems(living.all)) {\n issues.push({\n severity: \"error\",\n code: \"delta.living-requirement-id-invalid\",\n subject,\n message:\n problem.kind === \"invalid\"\n ? `${where}: the ${livingDoc} requirement '${problem.requirement}' has invalid Requirement-ID '${problem.value}', so this delta cannot select it safely`\n : problem.kind === \"repeated\"\n ? `${where}: the ${livingDoc} requirement '${problem.requirement}' declares Requirement-ID more than once, so this delta cannot select it safely`\n : `${where}: the ${livingDoc} shares Requirement-ID '${problem.id}' across ${problem.requirements.map((name) => `'${name}'`).join(\", \")}, so this delta cannot select one safely`,\n });\n }\n\n // Two living requirements under one heading make the delta algebra\n // disagree with itself on the SAME input: MODIFIED replaces the first\n // match (findIndex) while REMOVED deletes every match (filter). So one\n // delta edits one of the twins and leaves the other, and the next one\n // deletes both — and neither outcome is what an author reading the\n // living document could have predicted. Gating rather than advisory,\n // because there is no reading of the delta that makes the merge correct;\n // the fix is in the living document, and it is one rename.\n for (const [name, twins] of living.byName) {\n if (twins.length < 2) continue;\n issues.push({\n severity: \"error\",\n code: \"delta.living-duplicate-requirement\",\n subject,\n message: `${where}: the ${livingDoc} declares ${twins.length} requirements named '${name}' — MODIFIED would rewrite only the first and REMOVED would delete both, so no delta applies to it predictably. Give them distinct names (or distinct Requirement-IDs and headings) first.`,\n });\n }\n return issues;\n}\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The one (subject, axis) pass every delta check runs inside, and the living
3
+ * document indexed the way those checks ask about it.
4
+ *
5
+ * The scope exists because the checks that used to be one 400-line function all
6
+ * need the same five facts and none of them may take five parameters. It is a
7
+ * record of what is being graded, never of what was found — `Issue[]` stays a
8
+ * return value, so no check can quietly append to another's output.
9
+ *
10
+ * `indexLiving` builds all four lookups in one walk. Two of them answer the same
11
+ * question by different keys (heading, Requirement-ID) and the delta algebra
12
+ * requires them to agree; building one lazily beside the other is how they would
13
+ * eventually be built from different arrays.
14
+ */
15
+ import {} from "../document/spec.js";
16
+ export function indexLiving(living) {
17
+ const byName = new Map();
18
+ const byId = new Map();
19
+ for (const requirement of living) {
20
+ const named = byName.get(requirement.name) ?? [];
21
+ named.push(requirement);
22
+ byName.set(requirement.name, named);
23
+ if (requirement.id !== undefined) {
24
+ const identified = byId.get(requirement.id) ?? [];
25
+ identified.push(requirement);
26
+ byId.set(requirement.id, identified);
27
+ }
28
+ }
29
+ return {
30
+ names: new Set(living.map((r) => r.name)),
31
+ byName,
32
+ byId,
33
+ folded: new Map(living.map((r) => [r.name.toLowerCase(), r.name])),
34
+ all: living,
35
+ };
36
+ }
37
+ /** Key for the (kind, subject, axis, requirement) tuple a feature claims. The
38
+ * axes are separate namespaces on purpose — an arch requirement cannot conflict
39
+ * with a business requirement of the same name, they merge into different files
40
+ * — and so are the kinds, for the reason `ClaimSubject` states above. */
41
+ export function key(claim, identity) {
42
+ return `${claim.kind}\0${claim.subject}\0${claim.axis.file}\0${identity}`;
43
+ }
44
+ /** Stable ID when present; exact heading for a legacy requirement. */
45
+ export function requirementKey(requirement) {
46
+ return requirement.id === undefined ? `name:${requirement.name}` : `id:${requirement.id}`;
47
+ }
48
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../../src/core/delta/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAoB,MAAM,qBAAqB,CAAC;AA2DvD,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC9C,KAAK,MAAM,WAAW,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,WAAW,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;YAClD,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM;QACN,IAAI;QACJ,MAAM,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAU,CAAC,CAAC;QAC3E,GAAG,EAAE,MAAM;KACZ,CAAC;AACJ,CAAC;AAED;;;yEAGyE;AACzE,MAAM,UAAU,GAAG,CAAC,KAAmB,EAAE,QAAgB;IACvD,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;AAC5E,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAAC,WAA6C;IAC1E,OAAO,WAAW,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,EAAE,EAAE,CAAC;AAC5F,CAAC","sourcesContent":["/**\n * The one (subject, axis) pass every delta check runs inside, and the living\n * document indexed the way those checks ask about it.\n *\n * The scope exists because the checks that used to be one 400-line function all\n * need the same five facts and none of them may take five parameters. It is a\n * record of what is being graded, never of what was found — `Issue[]` stays a\n * return value, so no check can quietly append to another's output.\n *\n * `indexLiving` builds all four lookups in one walk. Two of them answer the same\n * question by different keys (heading, Requirement-ID) and the delta algebra\n * requires them to agree; building one lazily beside the other is how they would\n * eventually be built from different arrays.\n */\nimport { type Requirement } from \"../document/spec.js\";\n\n/**\n * Which file an axis is, at the grain the delta checks need — the filename and\n * nothing else, because that is all any of them read (`key` below is the only\n * consumer). Structurally a `SPEC_AXES` entry with its other fields dropped, so\n * the two service axes keep travelling exactly as before while the CAPABILITY\n * axis — which indexes no `ServicePaths` and therefore has no `key` — travels\n * too. `label` is deliberately NOT carried: nothing here reads it, and a field\n * that exists only to look like the record it came from is a field the next\n * reader tries to use in a message.\n */\nexport interface DeltaAxis {\n file: string;\n}\n\n/**\n * WHAT a delta claims, at the grain the claim registry is keyed by: the kind of\n * document, its identity, and which of its axes.\n *\n * `kind` is not decoration. A fleet may hold a service called `billing` and a\n * capability called `billing`, and both write requirements into a `spec.md`; a\n * key built from the name and the filename alone would let one feature's\n * service claim answer another feature's capability question — reporting\n * `delta.added-conflict` against a document it has nothing to do with.\n */\nexport interface ClaimSubject {\n kind: \"service\" | \"capability\";\n /** The service id, or the capability id — whichever `kind` says. */\n subject: string;\n axis: DeltaAxis;\n}\n\nexport interface DeltaScope extends ClaimSubject {\n /** The feature being graded — named in the `loam rebase <FEAT>` advice. */\n featureId: string;\n docsDir: string;\n /** The delta document this pass reads, for the one message that names a path. */\n specPath: string;\n /**\n * How this axis's file is named in messages. The arch file says which document\n * it means so a finding cannot be chased into the wrong one; spec.md keeps its\n * historical spelling.\n */\n where: string;\n /** How the axis's LIVING document is named in messages, same reasoning. */\n livingDoc: string;\n}\n\n/** The living requirements, keyed every way a delta may select one. */\nexport interface LivingIndex {\n names: Set<string>;\n byName: Map<string, Requirement[]>;\n byId: Map<string, Requirement[]>;\n /** Lowercased living name -> its exact spelling, for the near-duplicate warning. */\n folded: Map<string, string>;\n all: Requirement[];\n}\n\nexport function indexLiving(living: Requirement[]): LivingIndex {\n const byName = new Map<string, Requirement[]>();\n const byId = new Map<string, Requirement[]>();\n for (const requirement of living) {\n const named = byName.get(requirement.name) ?? [];\n named.push(requirement);\n byName.set(requirement.name, named);\n if (requirement.id !== undefined) {\n const identified = byId.get(requirement.id) ?? [];\n identified.push(requirement);\n byId.set(requirement.id, identified);\n }\n }\n return {\n names: new Set(living.map((r) => r.name)),\n byName,\n byId,\n folded: new Map(living.map((r) => [r.name.toLowerCase(), r.name] as const)),\n all: living,\n };\n}\n\n/** Key for the (kind, subject, axis, requirement) tuple a feature claims. The\n * axes are separate namespaces on purpose — an arch requirement cannot conflict\n * with a business requirement of the same name, they merge into different files\n * — and so are the kinds, for the reason `ClaimSubject` states above. */\nexport function key(claim: ClaimSubject, identity: string): string {\n return `${claim.kind}\\0${claim.subject}\\0${claim.axis.file}\\0${identity}`;\n}\n\n/** Stable ID when present; exact heading for a legacy requirement. */\nexport function requirementKey(requirement: Pick<Requirement, \"id\" | \"name\">): string {\n return requirement.id === undefined ? `name:${requirement.name}` : `id:${requirement.id}`;\n}\n"]}