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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1026) hide show
  1. package/CHANGELOG.md +2548 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +494 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +317 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +161 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +251 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +194 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +236 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +250 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +219 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +334 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +47 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +282 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +88 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  398. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  399. package/dist/core/c4/parsed/specification.js +78 -0
  400. package/dist/core/c4/parsed/specification.js.map +1 -0
  401. package/dist/core/c4/parsed/values.js +57 -0
  402. package/dist/core/c4/parsed/values.js.map +1 -0
  403. package/dist/core/c4/parsed/view-ids.js +75 -0
  404. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  405. package/dist/core/c4/project/architecture.js +45 -0
  406. package/dist/core/c4/project/architecture.js.map +1 -0
  407. package/dist/core/c4/project/documents.js +58 -0
  408. package/dist/core/c4/project/documents.js.map +1 -0
  409. package/dist/core/c4/project/load.js +183 -0
  410. package/dist/core/c4/project/load.js.map +1 -0
  411. package/dist/core/c4/resolve/service.js +113 -0
  412. package/dist/core/c4/resolve/service.js.map +1 -0
  413. package/dist/core/c4/seed/fleet-file.js +201 -0
  414. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  415. package/dist/core/c4/seed/items.js +91 -0
  416. package/dist/core/c4/seed/items.js.map +1 -0
  417. package/dist/core/c4/seed/stamp.js +48 -0
  418. package/dist/core/c4/seed/stamp.js.map +1 -0
  419. package/dist/core/c4/seed/template.js +109 -0
  420. package/dist/core/c4/seed/template.js.map +1 -0
  421. package/dist/core/c4/source-mask.js +82 -0
  422. package/dist/core/c4/source-mask.js.map +1 -0
  423. package/dist/core/c4/source-scan.js +227 -0
  424. package/dist/core/c4/source-scan.js.map +1 -0
  425. package/dist/core/c4/splice/authored-source.js +101 -0
  426. package/dist/core/c4/splice/authored-source.js.map +1 -0
  427. package/dist/core/c4/splice/contract.js +10 -0
  428. package/dist/core/c4/splice/contract.js.map +1 -0
  429. package/dist/core/c4/splice/delta-blocks.js +126 -0
  430. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  431. package/dist/core/c4/splice/identity/edges.js +36 -0
  432. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  433. package/dist/core/c4/splice/landscape-merge.js +269 -0
  434. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  435. package/dist/core/c4/splice/placement.js +194 -0
  436. package/dist/core/c4/splice/placement.js.map +1 -0
  437. package/dist/core/c4/workspace.js +172 -0
  438. package/dist/core/c4/workspace.js.map +1 -0
  439. package/dist/core/capabilities/authoring/notes.js +141 -0
  440. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  441. package/dist/core/capabilities/capabilities.js +124 -0
  442. package/dist/core/capabilities/capabilities.js.map +1 -0
  443. package/dist/core/capabilities/delta/doc.js +50 -0
  444. package/dist/core/capabilities/delta/doc.js.map +1 -0
  445. package/dist/core/capabilities/delta/overlay.js +129 -0
  446. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  447. package/dist/core/capabilities/delta/summary.js +57 -0
  448. package/dist/core/capabilities/delta/summary.js.map +1 -0
  449. package/dist/core/capabilities/delta/tree.js +43 -0
  450. package/dist/core/capabilities/delta/tree.js.map +1 -0
  451. package/dist/core/capabilities/delta/uncovered.js +222 -0
  452. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  453. package/dist/core/capabilities/findings.js +342 -0
  454. package/dist/core/capabilities/findings.js.map +1 -0
  455. package/dist/core/capabilities/realizes/findings.js +181 -0
  456. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  457. package/dist/core/capabilities/realizes/join.js +143 -0
  458. package/dist/core/capabilities/realizes/join.js.map +1 -0
  459. package/dist/core/capabilities/rollup.js +199 -0
  460. package/dist/core/capabilities/rollup.js.map +1 -0
  461. package/dist/core/capabilities/tree.js +126 -0
  462. package/dist/core/capabilities/tree.js.map +1 -0
  463. package/dist/core/capabilities/usecase-join.js +277 -0
  464. package/dist/core/capabilities/usecase-join.js.map +1 -0
  465. package/dist/core/coherence/authoring/scaffold.js +177 -0
  466. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  467. package/dist/core/coherence/authoring/sentinels.js +74 -0
  468. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  469. package/dist/core/coherence/coherence.js +251 -0
  470. package/dist/core/coherence/coherence.js.map +1 -0
  471. package/dist/core/coherence/declared.js +269 -0
  472. package/dist/core/coherence/declared.js.map +1 -0
  473. package/dist/core/coherence/events/declared.js +206 -0
  474. package/dist/core/coherence/events/declared.js.map +1 -0
  475. package/dist/core/coherence/events/events.js +204 -0
  476. package/dist/core/coherence/events/events.js.map +1 -0
  477. package/dist/core/coherence/events/grades.js +166 -0
  478. package/dist/core/coherence/events/grades.js.map +1 -0
  479. package/dist/core/coherence/events/lookups.js +122 -0
  480. package/dist/core/coherence/events/lookups.js.map +1 -0
  481. package/dist/core/coherence/events/merged.js +40 -0
  482. package/dist/core/coherence/events/merged.js.map +1 -0
  483. package/dist/core/coherence/living.js +198 -0
  484. package/dist/core/coherence/living.js.map +1 -0
  485. package/dist/core/coherence/lookups.js +155 -0
  486. package/dist/core/coherence/lookups.js.map +1 -0
  487. package/dist/core/coherence/pending.js +39 -0
  488. package/dist/core/coherence/pending.js.map +1 -0
  489. package/dist/core/conflict-markers.js +60 -0
  490. package/dist/core/conflict-markers.js.map +1 -0
  491. package/dist/core/delta/claims.js +73 -0
  492. package/dist/core/delta/claims.js.map +1 -0
  493. package/dist/core/delta/delta.js +296 -0
  494. package/dist/core/delta/delta.js.map +1 -0
  495. package/dist/core/delta/document.js +177 -0
  496. package/dist/core/delta/document.js.map +1 -0
  497. package/dist/core/delta/scope.js +48 -0
  498. package/dist/core/delta/scope.js.map +1 -0
  499. package/dist/core/delta/select.js +237 -0
  500. package/dist/core/delta/select.js.map +1 -0
  501. package/dist/core/dependencies/dependencies.js +141 -0
  502. package/dist/core/dependencies/dependencies.js.map +1 -0
  503. package/dist/core/dependencies/facts.js +165 -0
  504. package/dist/core/dependencies/facts.js.map +1 -0
  505. package/dist/core/dependencies/fanin.js +88 -0
  506. package/dist/core/dependencies/fanin.js.map +1 -0
  507. package/dist/core/dependencies/graph.js +106 -0
  508. package/dist/core/dependencies/graph.js.map +1 -0
  509. package/dist/core/diff/base-git.js +148 -0
  510. package/dist/core/diff/base-git.js.map +1 -0
  511. package/dist/core/diff/base-state.js +209 -0
  512. package/dist/core/diff/base-state.js.map +1 -0
  513. package/dist/core/diff/current-state.js +96 -0
  514. package/dist/core/diff/current-state.js.map +1 -0
  515. package/dist/core/diff/scope/changed-paths.js +188 -0
  516. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  517. package/dist/core/diff/semantic.js +243 -0
  518. package/dist/core/diff/semantic.js.map +1 -0
  519. package/dist/core/diff/victims.js +247 -0
  520. package/dist/core/diff/victims.js.map +1 -0
  521. package/dist/core/docs.js +7 -64
  522. package/dist/core/docs.js.map +1 -1
  523. package/dist/core/doctor/agents.js +222 -0
  524. package/dist/core/doctor/agents.js.map +1 -0
  525. package/dist/core/doctor/config.js +135 -0
  526. package/dist/core/doctor/config.js.map +1 -0
  527. package/dist/core/doctor/doctor.js +275 -0
  528. package/dist/core/doctor/doctor.js.map +1 -0
  529. package/dist/core/doctor/report.js +13 -0
  530. package/dist/core/doctor/report.js.map +1 -0
  531. package/dist/core/doctor/residue.js +110 -0
  532. package/dist/core/doctor/residue.js.map +1 -0
  533. package/dist/core/document/apply.js +77 -0
  534. package/dist/core/document/apply.js.map +1 -0
  535. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  536. package/dist/core/document/frontmatter.js.map +1 -0
  537. package/dist/core/document/grammar/directives.js +143 -0
  538. package/dist/core/document/grammar/directives.js.map +1 -0
  539. package/dist/core/document/parse.js +263 -0
  540. package/dist/core/document/parse.js.map +1 -0
  541. package/dist/core/document/scenarios.js +221 -0
  542. package/dist/core/document/scenarios.js.map +1 -0
  543. package/dist/core/document/spec.js +241 -0
  544. package/dist/core/document/spec.js.map +1 -0
  545. package/dist/core/{config.js → envelope/config.js} +79 -18
  546. package/dist/core/envelope/config.js.map +1 -0
  547. package/dist/core/envelope/json.js +175 -0
  548. package/dist/core/envelope/json.js.map +1 -0
  549. package/dist/core/{version.js → envelope/version.js} +3 -3
  550. package/dist/core/envelope/version.js.map +1 -0
  551. package/dist/core/explain/families.js +175 -0
  552. package/dist/core/explain/families.js.map +1 -0
  553. package/dist/core/explain/fix-tables.js +178 -0
  554. package/dist/core/explain/fix-tables.js.map +1 -0
  555. package/dist/core/explain/lookup.js +181 -0
  556. package/dist/core/explain/lookup.js.map +1 -0
  557. package/dist/core/explain/refusals.js +49 -0
  558. package/dist/core/explain/refusals.js.map +1 -0
  559. package/dist/core/explain/terms.js +144 -0
  560. package/dist/core/explain/terms.js.map +1 -0
  561. package/dist/core/explore/describe.js +105 -0
  562. package/dist/core/explore/describe.js.map +1 -0
  563. package/dist/core/{explore.js → explore/explore.js} +50 -101
  564. package/dist/core/explore/explore.js.map +1 -0
  565. package/dist/core/fleet-context.js +120 -78
  566. package/dist/core/fleet-context.js.map +1 -1
  567. package/dist/core/gate/checks.js +201 -0
  568. package/dist/core/gate/checks.js.map +1 -0
  569. package/dist/core/gate/partners.js +205 -0
  570. package/dist/core/gate/partners.js.map +1 -0
  571. package/dist/core/gate/report.js +47 -0
  572. package/dist/core/gate/report.js.map +1 -0
  573. package/dist/core/gate/verification.js +113 -0
  574. package/dist/core/gate/verification.js.map +1 -0
  575. package/dist/core/gherkin/digest.js +60 -0
  576. package/dist/core/gherkin/digest.js.map +1 -0
  577. package/dist/core/gherkin/emit.js +177 -0
  578. package/dist/core/gherkin/emit.js.map +1 -0
  579. package/dist/core/gherkin/read.js +76 -0
  580. package/dist/core/gherkin/read.js.map +1 -0
  581. package/dist/core/gherkin/stale.js +234 -0
  582. package/dist/core/gherkin/stale.js.map +1 -0
  583. package/dist/core/gherkin/stamp.js +77 -0
  584. package/dist/core/gherkin/stamp.js.map +1 -0
  585. package/dist/core/gherkin/steps/catalogue.js +154 -0
  586. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  587. package/dist/core/gherkin/steps/inventory.js +98 -0
  588. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  589. package/dist/core/gherkin/steps/phrase.js +72 -0
  590. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  591. package/dist/core/glossary/delta.js +106 -0
  592. package/dist/core/glossary/delta.js.map +1 -0
  593. package/dist/core/glossary/tree.js +86 -0
  594. package/dist/core/glossary/tree.js.map +1 -0
  595. package/dist/core/kernel/concurrency.js.map +1 -0
  596. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  597. package/dist/core/kernel/document-bytes.js.map +1 -0
  598. package/dist/core/kernel/fences.js +42 -0
  599. package/dist/core/kernel/fences.js.map +1 -0
  600. package/dist/core/kernel/ids/capability.js +72 -0
  601. package/dist/core/kernel/ids/capability.js.map +1 -0
  602. package/dist/core/kernel/ids/dirs.js +33 -0
  603. package/dist/core/kernel/ids/dirs.js.map +1 -0
  604. package/dist/core/kernel/ids/feature.js +75 -0
  605. package/dist/core/kernel/ids/feature.js.map +1 -0
  606. package/dist/core/kernel/ids/service.js +156 -0
  607. package/dist/core/kernel/ids/service.js.map +1 -0
  608. package/dist/core/kernel/ids/subsystem.js +63 -0
  609. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  610. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  611. package/dist/core/kernel/path-safety.js.map +1 -0
  612. package/dist/core/kernel/records.js.map +1 -0
  613. package/dist/core/links/backlinks.js +68 -0
  614. package/dist/core/links/backlinks.js.map +1 -0
  615. package/dist/core/links/case.js +74 -0
  616. package/dist/core/links/case.js.map +1 -0
  617. package/dist/core/links/corpus.js +104 -0
  618. package/dist/core/links/corpus.js.map +1 -0
  619. package/dist/core/links/findings.js +110 -0
  620. package/dist/core/links/findings.js.map +1 -0
  621. package/dist/core/links/parse.js +236 -0
  622. package/dist/core/links/parse.js.map +1 -0
  623. package/dist/core/mcp/argv.js +111 -0
  624. package/dist/core/mcp/argv.js.map +1 -0
  625. package/dist/core/mcp/author-tools.js +72 -0
  626. package/dist/core/mcp/author-tools.js.map +1 -0
  627. package/dist/core/mcp/framing.js +46 -0
  628. package/dist/core/mcp/framing.js.map +1 -0
  629. package/dist/core/mcp/protocol.js +280 -0
  630. package/dist/core/mcp/protocol.js.map +1 -0
  631. package/dist/core/mcp/tools.js +314 -0
  632. package/dist/core/mcp/tools.js.map +1 -0
  633. package/dist/core/obligations/obligations.js +106 -0
  634. package/dist/core/obligations/obligations.js.map +1 -0
  635. package/dist/core/openapi/baseline/gate.js +166 -0
  636. package/dist/core/openapi/baseline/gate.js.map +1 -0
  637. package/dist/core/openapi/baseline/plan.js +112 -0
  638. package/dist/core/openapi/baseline/plan.js.map +1 -0
  639. package/dist/core/openapi/baseline/record.js +175 -0
  640. package/dist/core/openapi/baseline/record.js.map +1 -0
  641. package/dist/core/openapi/depth.js +122 -0
  642. package/dist/core/openapi/depth.js.map +1 -0
  643. package/dist/core/openapi/digest.js +189 -0
  644. package/dist/core/openapi/digest.js.map +1 -0
  645. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  646. package/dist/core/openapi/doc.js.map +1 -0
  647. package/dist/core/openapi/merge/components.js +234 -0
  648. package/dist/core/openapi/merge/components.js.map +1 -0
  649. package/dist/core/openapi/merge/error.js +26 -0
  650. package/dist/core/openapi/merge/error.js.map +1 -0
  651. package/dist/core/openapi/merge/markers.js +208 -0
  652. package/dist/core/openapi/merge/markers.js.map +1 -0
  653. package/dist/core/openapi/merge/merge.js +256 -0
  654. package/dist/core/openapi/merge/merge.js.map +1 -0
  655. package/dist/core/openapi/merge/pin.js +128 -0
  656. package/dist/core/openapi/merge/pin.js.map +1 -0
  657. package/dist/core/openspec/decide/capabilities.js +244 -0
  658. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  659. package/dist/core/openspec/decide/dispositions.js +63 -0
  660. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  661. package/dist/core/openspec/decide/renames.js +179 -0
  662. package/dist/core/openspec/decide/renames.js.map +1 -0
  663. package/dist/core/openspec/decide/verdict.js +84 -0
  664. package/dist/core/openspec/decide/verdict.js.map +1 -0
  665. package/dist/core/openspec/inventory.js +221 -0
  666. package/dist/core/openspec/inventory.js.map +1 -0
  667. package/dist/core/openspec/model/mapping.js +76 -0
  668. package/dist/core/openspec/model/mapping.js.map +1 -0
  669. package/dist/core/openspec/model/model.js +65 -0
  670. package/dist/core/openspec/model/model.js.map +1 -0
  671. package/dist/core/openspec/scan/changes.js +235 -0
  672. package/dist/core/openspec/scan/changes.js.map +1 -0
  673. package/dist/core/openspec/scan/config.js +200 -0
  674. package/dist/core/openspec/scan/config.js.map +1 -0
  675. package/dist/core/openspec/scan/shape.js +260 -0
  676. package/dist/core/openspec/scan/shape.js.map +1 -0
  677. package/dist/core/openspec/scan/walk.js +133 -0
  678. package/dist/core/openspec/scan/walk.js.map +1 -0
  679. package/dist/core/openspec/scan/workspace.js +71 -0
  680. package/dist/core/openspec/scan/workspace.js.map +1 -0
  681. package/dist/core/owners/codeowners.js +141 -0
  682. package/dist/core/owners/codeowners.js.map +1 -0
  683. package/dist/core/pack/features.js +120 -0
  684. package/dist/core/pack/features.js.map +1 -0
  685. package/dist/core/pack/joins.js +112 -0
  686. package/dist/core/pack/joins.js.map +1 -0
  687. package/dist/core/pack/living.js +118 -0
  688. package/dist/core/pack/living.js.map +1 -0
  689. package/dist/core/pack/pack.js +76 -0
  690. package/dist/core/pack/pack.js.map +1 -0
  691. package/dist/core/permissions/permissions.js +87 -0
  692. package/dist/core/permissions/permissions.js.map +1 -0
  693. package/dist/core/projection/api.js +84 -0
  694. package/dist/core/projection/api.js.map +1 -0
  695. package/dist/core/projection/arch-slice.js +63 -0
  696. package/dist/core/projection/arch-slice.js.map +1 -0
  697. package/dist/core/projection/events.js +34 -0
  698. package/dist/core/projection/events.js.map +1 -0
  699. package/dist/core/provenance/findings.js +208 -0
  700. package/dist/core/provenance/findings.js.map +1 -0
  701. package/dist/core/provenance/git.js +225 -0
  702. package/dist/core/provenance/git.js.map +1 -0
  703. package/dist/core/provenance/gitq/moves.js +182 -0
  704. package/dist/core/provenance/gitq/moves.js.map +1 -0
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  707. package/dist/core/provenance/sample/scope.js +238 -0
  708. package/dist/core/provenance/sample/scope.js.map +1 -0
  709. package/dist/core/provenance/sample/sections.js +61 -0
  710. package/dist/core/provenance/sample/sections.js.map +1 -0
  711. package/dist/core/provenance/sources.js +263 -0
  712. package/dist/core/provenance/sources.js.map +1 -0
  713. package/dist/core/provenance/stamp.js +232 -0
  714. package/dist/core/provenance/stamp.js.map +1 -0
  715. package/dist/core/provenance/walk.js +157 -0
  716. package/dist/core/provenance/walk.js.map +1 -0
  717. package/dist/core/repo/authored/paths.js +192 -0
  718. package/dist/core/repo/authored/paths.js.map +1 -0
  719. package/dist/core/repo/entries.js +126 -0
  720. package/dist/core/repo/entries.js.map +1 -0
  721. package/dist/core/repo/paths.js +236 -0
  722. package/dist/core/repo/paths.js.map +1 -0
  723. package/dist/core/repo/repo.js +248 -0
  724. package/dist/core/repo/repo.js.map +1 -0
  725. package/dist/core/repo/service-target.js +153 -0
  726. package/dist/core/repo/service-target.js.map +1 -0
  727. package/dist/core/repo/state.js +58 -0
  728. package/dist/core/repo/state.js.map +1 -0
  729. package/dist/core/repo/tree/find.js +52 -0
  730. package/dist/core/repo/tree/find.js.map +1 -0
  731. package/dist/core/repo/tree/fs.js +64 -0
  732. package/dist/core/repo/tree/fs.js.map +1 -0
  733. package/dist/core/repo/tree/marker.js +61 -0
  734. package/dist/core/repo/tree/marker.js.map +1 -0
  735. package/dist/core/repo/tree/views.js +172 -0
  736. package/dist/core/repo/tree/views.js.map +1 -0
  737. package/dist/core/repo/tree/walk.js +234 -0
  738. package/dist/core/repo/tree/walk.js.map +1 -0
  739. package/dist/core/results.js +99 -2
  740. package/dist/core/results.js.map +1 -1
  741. package/dist/core/scaffold/landscape.js +131 -0
  742. package/dist/core/scaffold/landscape.js.map +1 -0
  743. package/dist/core/scaffold/readme.js +84 -0
  744. package/dist/core/scaffold/readme.js.map +1 -0
  745. package/dist/core/staging/commit.js +241 -0
  746. package/dist/core/staging/commit.js.map +1 -0
  747. package/dist/core/staging/interrupted.js +96 -0
  748. package/dist/core/staging/interrupted.js.map +1 -0
  749. package/dist/core/staging/lock.js +206 -0
  750. package/dist/core/staging/lock.js.map +1 -0
  751. package/dist/core/staging/recovery/finding.js +35 -0
  752. package/dist/core/staging/recovery/finding.js.map +1 -0
  753. package/dist/core/staging/recovery/intent.js +98 -0
  754. package/dist/core/staging/recovery/intent.js.map +1 -0
  755. package/dist/core/staging/recovery/recover.js +126 -0
  756. package/dist/core/staging/recovery/recover.js.map +1 -0
  757. package/dist/core/staging/recovery/residue.js +62 -0
  758. package/dist/core/staging/recovery/residue.js.map +1 -0
  759. package/dist/core/staging/snapshot.js +202 -0
  760. package/dist/core/staging/snapshot.js.map +1 -0
  761. package/dist/core/staging/txn/forward.js +171 -0
  762. package/dist/core/staging/txn/forward.js.map +1 -0
  763. package/dist/core/staging/txn/journal.js +139 -0
  764. package/dist/core/staging/txn/journal.js.map +1 -0
  765. package/dist/core/staging/txn/transaction.js +75 -0
  766. package/dist/core/staging/txn/transaction.js.map +1 -0
  767. package/dist/core/staging/writes.js +113 -0
  768. package/dist/core/staging/writes.js.map +1 -0
  769. package/dist/core/status/actions/execution.js +49 -0
  770. package/dist/core/status/actions/execution.js.map +1 -0
  771. package/dist/core/status/contracts.js +102 -0
  772. package/dist/core/status/contracts.js.map +1 -0
  773. package/dist/core/status/feature/artifacts.js +171 -0
  774. package/dist/core/status/feature/artifacts.js.map +1 -0
  775. package/dist/core/status/feature/feature.js +207 -0
  776. package/dist/core/status/feature/feature.js.map +1 -0
  777. package/dist/core/status/feature/next.js +150 -0
  778. package/dist/core/status/feature/next.js.map +1 -0
  779. package/dist/core/status/feature/state.js +21 -0
  780. package/dist/core/status/feature/state.js.map +1 -0
  781. package/dist/core/status/fleet/fleet.js +187 -0
  782. package/dist/core/status/fleet/fleet.js.map +1 -0
  783. package/dist/core/status/fleet/next.js +207 -0
  784. package/dist/core/status/fleet/next.js.map +1 -0
  785. package/dist/core/status/interrupted.js +97 -0
  786. package/dist/core/status/interrupted.js.map +1 -0
  787. package/dist/core/status/report.js +31 -0
  788. package/dist/core/status/report.js.map +1 -0
  789. package/dist/core/status/scan.js +41 -0
  790. package/dist/core/status/scan.js.map +1 -0
  791. package/dist/core/status/verification.js +201 -0
  792. package/dist/core/status/verification.js.map +1 -0
  793. package/dist/core/usecases/capability.js +169 -0
  794. package/dist/core/usecases/capability.js.map +1 -0
  795. package/dist/core/usecases/delta/claims.js +116 -0
  796. package/dist/core/usecases/delta/claims.js.map +1 -0
  797. package/dist/core/usecases/delta/flows.js +103 -0
  798. package/dist/core/usecases/delta/flows.js.map +1 -0
  799. package/dist/core/usecases/delta/overlay.js +235 -0
  800. package/dist/core/usecases/delta/overlay.js.map +1 -0
  801. package/dist/core/usecases/fleet.js +137 -0
  802. package/dist/core/usecases/fleet.js.map +1 -0
  803. package/dist/core/usecases/operations.js +111 -0
  804. package/dist/core/usecases/operations.js.map +1 -0
  805. package/dist/core/usecases/place.js +44 -0
  806. package/dist/core/usecases/place.js.map +1 -0
  807. package/dist/core/usecases/touch.js +101 -0
  808. package/dist/core/usecases/touch.js.map +1 -0
  809. package/dist/core/verify/answers.js +173 -0
  810. package/dist/core/verify/answers.js.map +1 -0
  811. package/dist/core/verify/build.js +150 -0
  812. package/dist/core/verify/build.js.map +1 -0
  813. package/dist/core/verify/checklist.js +201 -0
  814. package/dist/core/verify/checklist.js.map +1 -0
  815. package/dist/core/verify/claims/identity.js +61 -0
  816. package/dist/core/verify/claims/identity.js.map +1 -0
  817. package/dist/core/verify/cross/diff.js +97 -0
  818. package/dist/core/verify/cross/diff.js.map +1 -0
  819. package/dist/core/verify/evidence/contract.js +169 -0
  820. package/dist/core/verify/evidence/contract.js.map +1 -0
  821. package/dist/core/verify/file.js +257 -0
  822. package/dist/core/verify/file.js.map +1 -0
  823. package/dist/core/verify/pins/lint.js +157 -0
  824. package/dist/core/verify/pins/lint.js.map +1 -0
  825. package/dist/core/verify/pins/pin.js +95 -0
  826. package/dist/core/verify/pins/pin.js.map +1 -0
  827. package/dist/core/verify/record.js +142 -0
  828. package/dist/core/verify/record.js.map +1 -0
  829. package/dist/core/verify/store/commit.js +89 -0
  830. package/dist/core/verify/store/commit.js.map +1 -0
  831. package/dist/core/verify/store/render.js +57 -0
  832. package/dist/core/verify/store/render.js.map +1 -0
  833. package/dist/core/vocabulary/health.js +91 -0
  834. package/dist/core/vocabulary/health.js.map +1 -0
  835. package/dist/core/vocabulary/issue.js +55 -0
  836. package/dist/core/vocabulary/issue.js.map +1 -0
  837. package/dist/core/vocabulary/maturity.js +145 -0
  838. package/dist/core/vocabulary/maturity.js.map +1 -0
  839. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  840. package/dist/core/vocabulary/report.js.map +1 -0
  841. package/dist/core/vocabulary/steps.js +266 -0
  842. package/dist/core/vocabulary/steps.js.map +1 -0
  843. package/dist/core/workspace/discover.js +239 -0
  844. package/dist/core/workspace/discover.js.map +1 -0
  845. package/dist/core/workspace/render.js +52 -0
  846. package/dist/core/workspace/render.js.map +1 -0
  847. package/examples/README.md +189 -0
  848. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  849. package/examples/docs/architecture/capabilities.yaml +31 -0
  850. package/examples/docs/architecture/landscape.likec4 +280 -0
  851. package/examples/docs/architecture/obligations.yaml +23 -0
  852. package/examples/docs/architecture/permissions.yaml +50 -0
  853. package/examples/docs/architecture/subsystems.likec4 +11 -0
  854. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  855. package/examples/docs/capabilities/checkout/spec.md +55 -0
  856. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  857. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  858. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  859. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  860. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  863. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  864. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  865. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  866. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  867. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  868. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  869. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  870. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  871. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  872. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  873. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  874. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  875. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  876. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  877. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  878. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  879. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  882. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  883. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  884. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  885. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  886. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  887. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  888. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  889. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  890. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  891. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  896. package/examples/docs/glossary/order.md +15 -0
  897. package/examples/docs/glossary/payments/authorization.md +14 -0
  898. package/examples/docs/likec4.config.json +9 -0
  899. package/examples/docs/loam.json +3 -0
  900. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  901. package/examples/docs/services/checkout-web/spec.md +91 -0
  902. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  903. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  904. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  905. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  906. package/examples/docs/services/order-service/arch.spec.md +127 -0
  907. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  908. package/examples/docs/services/order-service/health.yaml +57 -0
  909. package/examples/docs/services/order-service/model.likec4 +138 -0
  910. package/examples/docs/services/order-service/openapi.yaml +257 -0
  911. package/examples/docs/services/order-service/runbook.md +62 -0
  912. package/examples/docs/services/order-service/spec.md +134 -0
  913. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  914. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  915. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  916. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  917. package/examples/docs/services/payment-service/health.yaml +39 -0
  918. package/examples/docs/services/payment-service/model.likec4 +67 -0
  919. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  920. package/examples/docs/services/payment-service/runbook.md +37 -0
  921. package/examples/docs/services/payment-service/spec.md +88 -0
  922. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  923. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  924. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  925. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  926. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  927. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  928. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  929. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  930. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  931. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  932. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  933. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  934. package/examples/docs/services/platform/subsystem.yaml +2 -0
  935. package/examples/fleet.yaml +57 -0
  936. package/package.json +19 -6
  937. package/dist/commands/adopt.js +0 -240
  938. package/dist/commands/adopt.js.map +0 -1
  939. package/dist/commands/archive.js +0 -1313
  940. package/dist/commands/archive.js.map +0 -1
  941. package/dist/commands/delta.js +0 -388
  942. package/dist/commands/delta.js.map +0 -1
  943. package/dist/commands/docs-repo-gate.js.map +0 -1
  944. package/dist/commands/format.js +0 -17
  945. package/dist/commands/format.js.map +0 -1
  946. package/dist/commands/gherkin.js +0 -309
  947. package/dist/commands/gherkin.js.map +0 -1
  948. package/dist/commands/init.js.map +0 -1
  949. package/dist/commands/list.js +0 -295
  950. package/dist/commands/list.js.map +0 -1
  951. package/dist/commands/migrate-openspec.js +0 -950
  952. package/dist/commands/migrate-openspec.js.map +0 -1
  953. package/dist/commands/new.js +0 -413
  954. package/dist/commands/new.js.map +0 -1
  955. package/dist/commands/rebase.js +0 -356
  956. package/dist/commands/rebase.js.map +0 -1
  957. package/dist/commands/show.js.map +0 -1
  958. package/dist/commands/status.js +0 -262
  959. package/dist/commands/status.js.map +0 -1
  960. package/dist/commands/unarchive.js.map +0 -1
  961. package/dist/commands/validate.js +0 -1773
  962. package/dist/commands/validate.js.map +0 -1
  963. package/dist/commands/verify.js +0 -775
  964. package/dist/commands/verify.js.map +0 -1
  965. package/dist/commands/vouch.js +0 -421
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2173
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js.map +0 -1
  970. package/dist/core/arch.js +0 -130
  971. package/dist/core/arch.js.map +0 -1
  972. package/dist/core/asyncapi.js.map +0 -1
  973. package/dist/core/brief.js +0 -755
  974. package/dist/core/brief.js.map +0 -1
  975. package/dist/core/coherence.js +0 -577
  976. package/dist/core/coherence.js.map +0 -1
  977. package/dist/core/concurrency.js.map +0 -1
  978. package/dist/core/config.js.map +0 -1
  979. package/dist/core/delta.js +0 -476
  980. package/dist/core/delta.js.map +0 -1
  981. package/dist/core/dependencies.js +0 -356
  982. package/dist/core/dependencies.js.map +0 -1
  983. package/dist/core/doctor.js +0 -596
  984. package/dist/core/doctor.js.map +0 -1
  985. package/dist/core/document-bytes.js.map +0 -1
  986. package/dist/core/explore.js.map +0 -1
  987. package/dist/core/frontmatter.js.map +0 -1
  988. package/dist/core/gherkin.js +0 -463
  989. package/dist/core/gherkin.js.map +0 -1
  990. package/dist/core/health.js +0 -67
  991. package/dist/core/health.js.map +0 -1
  992. package/dist/core/ids.js +0 -122
  993. package/dist/core/ids.js.map +0 -1
  994. package/dist/core/issue.js +0 -9
  995. package/dist/core/issue.js.map +0 -1
  996. package/dist/core/json.js +0 -74
  997. package/dist/core/json.js.map +0 -1
  998. package/dist/core/likec4.js +0 -489
  999. package/dist/core/likec4.js.map +0 -1
  1000. package/dist/core/maturity.js +0 -66
  1001. package/dist/core/maturity.js.map +0 -1
  1002. package/dist/core/openapi-merge.js +0 -552
  1003. package/dist/core/openapi-merge.js.map +0 -1
  1004. package/dist/core/openapi.js.map +0 -1
  1005. package/dist/core/openspec-inventory.js +0 -1440
  1006. package/dist/core/openspec-inventory.js.map +0 -1
  1007. package/dist/core/path-safety.js.map +0 -1
  1008. package/dist/core/provenance.js +0 -787
  1009. package/dist/core/provenance.js.map +0 -1
  1010. package/dist/core/records.js.map +0 -1
  1011. package/dist/core/repo.js +0 -438
  1012. package/dist/core/repo.js.map +0 -1
  1013. package/dist/core/report.js.map +0 -1
  1014. package/dist/core/spec.js +0 -505
  1015. package/dist/core/spec.js.map +0 -1
  1016. package/dist/core/staging.js +0 -890
  1017. package/dist/core/staging.js.map +0 -1
  1018. package/dist/core/status.js +0 -1132
  1019. package/dist/core/status.js.map +0 -1
  1020. package/dist/core/steps.js +0 -72
  1021. package/dist/core/steps.js.map +0 -1
  1022. package/dist/core/verify.js +0 -807
  1023. package/dist/core/verify.js.map +0 -1
  1024. package/dist/core/version.js.map +0 -1
  1025. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1026. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The baseline axis of the feature gate: which of the surfaces this delta
3
+ * SPELLS does it actually edit? A feature's openapi.yaml is a complete
4
+ * document, so most of it is quotation — and until the pins existed the merge
5
+ * upserted quotations too, which reverted whatever had landed on them since.
6
+ *
7
+ * Extracted from `coherence/declared.ts` when the operation pin grew its two
8
+ * sibling surfaces (path-item non-method keys, components): the grading is one
9
+ * subject with three families, and coherence/ is at its file limit. Everything
10
+ * here is graded by the ONE classifier in `../digest.ts` over the ONE surface
11
+ * enumeration in `./record.ts`, so the gate, the rebase plan and the merge
12
+ * cannot drift into different ideas of what a delta restates.
13
+ */
14
+ import { parse } from "yaml";
15
+ import { classifyBaselineDigests, OPENAPI_BASELINE_KEY, OPENAPI_BASELINES_KEY, OPERATION_DIGEST_LENGTH, OPERATION_DIGEST_RE, valueDigest, } from "../digest.js";
16
+ import { entryFor, readBaselineRecord, restatedSurfaces, surfaceIn } from "./record.js";
17
+ /**
18
+ * Grade every baseline this feature contract carries — operation pins,
19
+ * path-item keys, components — against the living contract. Slot-keyed for
20
+ * operations, (path, key)-keyed and (kind/name)-keyed for the record, exactly
21
+ * as the merge writes. With no living side there are no surface checks at all:
22
+ * everything the delta spells is new, and a baseline only means something for
23
+ * a value that already exists.
24
+ */
25
+ export function openapiBaselineIssues(input) {
26
+ const { featDoc, livingDoc, service, featureId } = input;
27
+ const issues = [];
28
+ const livingBySlot = new Map(livingDoc.ops.map((op) => [`${op.method} ${op.path}`, op]));
29
+ let unpinned = 0;
30
+ for (const op of featDoc.ops) {
31
+ // A removal marker is not a restatement of anything; its own exactness
32
+ // check (`openapi.remove-target-mismatch`) already guards the slot.
33
+ if (op.remove)
34
+ continue;
35
+ const where = `${op.method.toUpperCase()} ${op.path}`;
36
+ const livingOp = livingBySlot.get(`${op.method} ${op.path}`);
37
+ if (op.basedOn !== undefined && !OPERATION_DIGEST_RE.test(op.basedOn)) {
38
+ issues.push({
39
+ severity: "error",
40
+ code: "openapi.baseline-invalid",
41
+ subject: service,
42
+ message: `${service}: ${where} has invalid ${OPENAPI_BASELINE_KEY} '${op.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \`loam rebase\` writes them`,
43
+ });
44
+ continue;
45
+ }
46
+ const verdict = classifyBaselineDigests(op.basedOn, op.digest, livingOp?.digest);
47
+ if (verdict === "unfounded") {
48
+ issues.push({
49
+ severity: "error",
50
+ code: "openapi.baseline-invalid",
51
+ subject: service,
52
+ message: `${service}: ${where} ('${op.id}') carries ${OPENAPI_BASELINE_KEY}, but the living contract has no operation at that slot — a new operation has no living version to be based on; drop the marker`,
53
+ });
54
+ }
55
+ else if (verdict === "stale") {
56
+ issues.push({
57
+ severity: "error",
58
+ code: "openapi.baseline-stale",
59
+ subject: service,
60
+ message: `${service}: ${where} ('${op.id}') was written against living version ${op.basedOn}, but the living contract now holds ${livingOp.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living operation, fold in what you still mean, then run \`loam rebase ${featureId}\`.`,
61
+ });
62
+ }
63
+ else if (verdict === "unpinned" && livingOp !== undefined) {
64
+ // Counted, not listed. A delta over a thirty-operation service quotes
65
+ // twenty-nine of them, and twenty-nine identical warnings naming a fix
66
+ // that is ONE command teaches people to filter the code out.
67
+ unpinned += 1;
68
+ }
69
+ }
70
+ // The two surfaces the operation pin cannot reach, graded from the
71
+ // `x-loam-baselines` record by the same classifier. Unreadable documents
72
+ // never get here on the feature side (`openapi.invalid` suspends the axis);
73
+ // an unreadable LIVING side is that contract's own `openapi.invalid`, and
74
+ // grading against its empty parse would call every restatement new.
75
+ const missing = { keys: 0, components: 0 };
76
+ if (input.livingText !== undefined && !featDoc.unreadable && !livingDoc.unreadable) {
77
+ const featPlain = parse(input.featureText);
78
+ const livingPlain = parse(input.livingText);
79
+ const { record, problems } = readBaselineRecord(featPlain);
80
+ for (const problem of problems) {
81
+ issues.push({
82
+ severity: "error",
83
+ code: "openapi.baseline-invalid",
84
+ subject: service,
85
+ message: `${service}: ${problem} — the record is \`loam rebase\`'s bookkeeping and cannot be graded as it stands; re-run \`loam rebase ${featureId}\` to rebuild it`,
86
+ });
87
+ }
88
+ const surfaces = restatedSurfaces(featPlain);
89
+ // An entry pinning a surface the delta does not restate is a claim about
90
+ // nothing — usually a surface the author deleted after rebasing. Distinct
91
+ // from `unfounded` below, where the surface IS restated and the LIVING
92
+ // side is what vanished.
93
+ const restated = new Set(surfaces.map((s) => (s.kind === "path-item" ? `${s.path}\0${s.key}` : s.id)));
94
+ for (const [path, keys] of Object.entries(record.pathItems)) {
95
+ for (const key of Object.keys(keys)) {
96
+ if (restated.has(`${path}\0${key}`))
97
+ continue;
98
+ issues.push({
99
+ severity: "error",
100
+ code: "openapi.baseline-invalid",
101
+ subject: service,
102
+ message: `${service}: ${OPENAPI_BASELINES_KEY} pins '${key}' (${path}), but this feature's openapi.yaml does not restate that key — re-run \`loam rebase ${featureId}\` to rebuild the record`,
103
+ });
104
+ }
105
+ }
106
+ for (const id of Object.keys(record.components)) {
107
+ if (restated.has(id))
108
+ continue;
109
+ issues.push({
110
+ severity: "error",
111
+ code: "openapi.baseline-invalid",
112
+ subject: service,
113
+ message: `${service}: ${OPENAPI_BASELINES_KEY} pins component '${id}', but this feature's openapi.yaml does not declare it — re-run \`loam rebase ${featureId}\` to rebuild the record`,
114
+ });
115
+ }
116
+ for (const s of surfaces) {
117
+ const label = s.kind === "path-item" ? `path-level key '${s.key}' (${s.path})` : `component '${s.id}'`;
118
+ const entry = entryFor(record, s);
119
+ const living = surfaceIn(livingPlain, s);
120
+ const livingDigest = living.found ? valueDigest(living.value) : undefined;
121
+ const verdict = classifyBaselineDigests(entry, valueDigest(s.value), livingDigest);
122
+ if (verdict === "unpinned") {
123
+ if (living.found) {
124
+ if (s.kind === "path-item")
125
+ missing.keys += 1;
126
+ else
127
+ missing.components += 1;
128
+ }
129
+ }
130
+ else if (verdict === "stale") {
131
+ issues.push({
132
+ severity: "error",
133
+ code: "openapi.baseline-stale",
134
+ subject: service,
135
+ message: `${service}: ${label} was pinned to living version ${entry}, but the living contract now holds ${livingDigest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \`loam rebase ${featureId}\`.`,
136
+ });
137
+ }
138
+ else if (verdict === "unfounded") {
139
+ issues.push({
140
+ severity: "error",
141
+ code: "openapi.baseline-invalid",
142
+ subject: service,
143
+ message: `${service}: ${label} is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \`loam rebase ${featureId}\``,
144
+ });
145
+ }
146
+ }
147
+ }
148
+ if (unpinned > 0 || missing.keys > 0 || missing.components > 0) {
149
+ // A warning that GATES archive, same doctrine as `delta.baseline-missing`
150
+ // (delta/select.ts): the document is legal, but an unpinned quote is
151
+ // exactly the shape that reverts another feature's landed change at exit
152
+ // 0, and every count covers only values the living contract already has —
153
+ // a genuinely new operation, key or component never trips it. `loam
154
+ // rebase` pins a whole service in one command; `--approve` says the
155
+ // unpinned merge is meant.
156
+ issues.push({
157
+ severity: "warn",
158
+ gates: true,
159
+ code: "openapi.baseline-missing",
160
+ subject: service,
161
+ message: `${service}: ${unpinned} operation(s), ${missing.keys} path-level key(s) and ${missing.components} component(s) in this feature's openapi.yaml carry no baseline pin (${OPENAPI_BASELINE_KEY} / ${OPENAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \`loam rebase ${featureId} --service ${service}\`, or archive with --approve to merge unpinned deliberately.`,
162
+ });
163
+ }
164
+ return issues;
165
+ }
166
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../../src/core/openapi/baseline/gate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The baseline axis of the feature gate: which of the surfaces this delta\n * SPELLS does it actually edit? A feature's openapi.yaml is a complete\n * document, so most of it is quotation — and until the pins existed the merge\n * upserted quotations too, which reverted whatever had landed on them since.\n *\n * Extracted from `coherence/declared.ts` when the operation pin grew its two\n * sibling surfaces (path-item non-method keys, components): the grading is one\n * subject with three families, and coherence/ is at its file limit. Everything\n * here is graded by the ONE classifier in `../digest.ts` over the ONE surface\n * enumeration in `./record.ts`, so the gate, the rebase plan and the merge\n * cannot drift into different ideas of what a delta restates.\n */\nimport { parse } from \"yaml\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport type { OpenapiDoc } from \"../doc.js\";\nimport {\n classifyBaselineDigests,\n OPENAPI_BASELINE_KEY,\n OPENAPI_BASELINES_KEY,\n OPERATION_DIGEST_LENGTH,\n OPERATION_DIGEST_RE,\n valueDigest,\n} from \"../digest.js\";\nimport { entryFor, readBaselineRecord, restatedSurfaces, surfaceIn } from \"./record.js\";\n\n/** Everything the gate needs about one service's pair of contracts. */\nexport interface BaselineGateInput {\n /** The FEATURE contract as `readOpenapi` parsed it — ops carry digests and pins. */\n featDoc: OpenapiDoc;\n /** The LIVING contract's parse; empty when the file is absent. */\n livingDoc: OpenapiDoc;\n /** The feature contract's text — the surface record and values are read from it. */\n featureText: string;\n /** The living contract's text, or undefined when no living contract exists. */\n livingText: string | undefined;\n service: string;\n featureId: string;\n}\n\n/**\n * Grade every baseline this feature contract carries — operation pins,\n * path-item keys, components — against the living contract. Slot-keyed for\n * operations, (path, key)-keyed and (kind/name)-keyed for the record, exactly\n * as the merge writes. With no living side there are no surface checks at all:\n * everything the delta spells is new, and a baseline only means something for\n * a value that already exists.\n */\nexport function openapiBaselineIssues(input: BaselineGateInput): Issue[] {\n const { featDoc, livingDoc, service, featureId } = input;\n const issues: Issue[] = [];\n const livingBySlot = new Map(livingDoc.ops.map((op) => [`${op.method} ${op.path}`, op]));\n let unpinned = 0;\n for (const op of featDoc.ops) {\n // A removal marker is not a restatement of anything; its own exactness\n // check (`openapi.remove-target-mismatch`) already guards the slot.\n if (op.remove) continue;\n const where = `${op.method.toUpperCase()} ${op.path}`;\n const livingOp = livingBySlot.get(`${op.method} ${op.path}`);\n if (op.basedOn !== undefined && !OPERATION_DIGEST_RE.test(op.basedOn)) {\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-invalid\",\n subject: service,\n message: `${service}: ${where} has invalid ${OPENAPI_BASELINE_KEY} '${op.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n continue;\n }\n const verdict = classifyBaselineDigests(op.basedOn, op.digest, livingOp?.digest);\n if (verdict === \"unfounded\") {\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-invalid\",\n subject: service,\n message: `${service}: ${where} ('${op.id}') carries ${OPENAPI_BASELINE_KEY}, but the living contract has no operation at that slot — a new operation has no living version to be based on; drop the marker`,\n });\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-stale\",\n subject: service,\n message: `${service}: ${where} ('${op.id}') was written against living version ${op.basedOn}, but the living contract now holds ${livingOp!.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living operation, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unpinned\" && livingOp !== undefined) {\n // Counted, not listed. A delta over a thirty-operation service quotes\n // twenty-nine of them, and twenty-nine identical warnings naming a fix\n // that is ONE command teaches people to filter the code out.\n unpinned += 1;\n }\n }\n\n // The two surfaces the operation pin cannot reach, graded from the\n // `x-loam-baselines` record by the same classifier. Unreadable documents\n // never get here on the feature side (`openapi.invalid` suspends the axis);\n // an unreadable LIVING side is that contract's own `openapi.invalid`, and\n // grading against its empty parse would call every restatement new.\n const missing = { keys: 0, components: 0 };\n if (input.livingText !== undefined && !featDoc.unreadable && !livingDoc.unreadable) {\n const featPlain: unknown = parse(input.featureText);\n const livingPlain: unknown = parse(input.livingText);\n const { record, problems } = readBaselineRecord(featPlain);\n for (const problem of problems) {\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-invalid\",\n subject: service,\n message: `${service}: ${problem} — the record is \\`loam rebase\\`'s bookkeeping and cannot be graded as it stands; re-run \\`loam rebase ${featureId}\\` to rebuild it`,\n });\n }\n const surfaces = restatedSurfaces(featPlain);\n // An entry pinning a surface the delta does not restate is a claim about\n // nothing — usually a surface the author deleted after rebasing. Distinct\n // from `unfounded` below, where the surface IS restated and the LIVING\n // side is what vanished.\n const restated = new Set(surfaces.map((s) => (s.kind === \"path-item\" ? `${s.path}\\0${s.key}` : s.id)));\n for (const [path, keys] of Object.entries(record.pathItems)) {\n for (const key of Object.keys(keys)) {\n if (restated.has(`${path}\\0${key}`)) continue;\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-invalid\",\n subject: service,\n message: `${service}: ${OPENAPI_BASELINES_KEY} pins '${key}' (${path}), but this feature's openapi.yaml does not restate that key — re-run \\`loam rebase ${featureId}\\` to rebuild the record`,\n });\n }\n }\n for (const id of Object.keys(record.components)) {\n if (restated.has(id)) continue;\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-invalid\",\n subject: service,\n message: `${service}: ${OPENAPI_BASELINES_KEY} pins component '${id}', but this feature's openapi.yaml does not declare it — re-run \\`loam rebase ${featureId}\\` to rebuild the record`,\n });\n }\n for (const s of surfaces) {\n const label = s.kind === \"path-item\" ? `path-level key '${s.key}' (${s.path})` : `component '${s.id}'`;\n const entry = entryFor(record, s);\n const living = surfaceIn(livingPlain, s);\n const livingDigest = living.found ? valueDigest(living.value) : undefined;\n const verdict = classifyBaselineDigests(entry, valueDigest(s.value), livingDigest);\n if (verdict === \"unpinned\") {\n if (living.found) {\n if (s.kind === \"path-item\") missing.keys += 1;\n else missing.components += 1;\n }\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-stale\",\n subject: service,\n message: `${service}: ${label} was pinned to living version ${entry}, but the living contract now holds ${livingDigest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unfounded\") {\n issues.push({\n severity: \"error\",\n code: \"openapi.baseline-invalid\",\n subject: service,\n message: `${service}: ${label} is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \\`loam rebase ${featureId}\\``,\n });\n }\n }\n }\n\n if (unpinned > 0 || missing.keys > 0 || missing.components > 0) {\n // A warning that GATES archive, same doctrine as `delta.baseline-missing`\n // (delta/select.ts): the document is legal, but an unpinned quote is\n // exactly the shape that reverts another feature's landed change at exit\n // 0, and every count covers only values the living contract already has —\n // a genuinely new operation, key or component never trips it. `loam\n // rebase` pins a whole service in one command; `--approve` says the\n // unpinned merge is meant.\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"openapi.baseline-missing\",\n subject: service,\n message: `${service}: ${unpinned} operation(s), ${missing.keys} path-level key(s) and ${missing.components} component(s) in this feature's openapi.yaml carry no baseline pin (${OPENAPI_BASELINE_KEY} / ${OPENAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \\`loam rebase ${featureId} --service ${service}\\`, or archive with --approve to merge unpinned deliberately.`,\n });\n }\n return issues;\n}\n"]}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * What `loam rebase` writes on the surface axis: the `x-loam-baselines` record,
3
+ * rebuilt wholesale from what is on disk right now.
4
+ *
5
+ * The record is rebuilt rather than patched for the same reason the operation
6
+ * pin is always the LIVING digest: a pin is a claim about what its author read,
7
+ * and an entry left behind for a surface the delta no longer restates — or one
8
+ * whose living counterpart is gone — is a claim nobody is making any more.
9
+ * Wholesale rebuild prunes both without a second code path, and the sorted
10
+ * serialization (`buildRecord`) is what makes a second run byte-identical.
11
+ *
12
+ * This module must not import from `../merge/` — the merge reads
13
+ * `./record.ts`, so a value edge back into `merge/` would be a package cycle.
14
+ * That is also why an unreadable document is answered with "nothing to pin"
15
+ * instead of `OpenapiMergeError`: every caller chains this AFTER
16
+ * `pinOpenapiOperations`, which has already thrown that error for any document
17
+ * either side cannot parse, so the silent answer here is unreachable in
18
+ * practice and merely keeps the failure ownership where it already lives.
19
+ */
20
+ import { isDeepStrictEqual } from "node:util";
21
+ import { parseDocument } from "yaml";
22
+ import { isRecord } from "../../kernel/records.js";
23
+ import { OPENAPI_BASELINES_KEY, valueDigest } from "../digest.js";
24
+ import { buildRecord, entryFor, readBaselineRecord, restatedSurfaces, surfaceIn } from "./record.js";
25
+ /**
26
+ * Pin every restated path-item key and component of a feature contract against
27
+ * the living one. The entry is always `valueDigest` of the LIVING value, never
28
+ * of the delta's own — the one rule that yields both merge verdicts: a surface
29
+ * the author only QUOTED equals its own entry, one they EDITED differs from it.
30
+ * A surface with no living counterpart is `unresolved` and deliberately absent
31
+ * from the record: there is no baseline to claim, and inventing one would make
32
+ * `classifyBaselineDigests` call a genuinely new value `unfounded`.
33
+ */
34
+ export function planOpenapiBaselines(featureText, livingText, _service) {
35
+ const doc = parseDocument(featureText);
36
+ if (doc.errors.length > 0)
37
+ return { text: null, pins: [] };
38
+ let featPlain;
39
+ let livingPlain;
40
+ try {
41
+ featPlain = doc.toJS() ?? {};
42
+ const living = parseDocument(livingText);
43
+ if (living.errors.length > 0)
44
+ return { text: null, pins: [] };
45
+ livingPlain = living.toJS() ?? {};
46
+ }
47
+ catch {
48
+ return { text: null, pins: [] };
49
+ }
50
+ const current = readBaselineRecord(featPlain).record;
51
+ const pathItems = new Map();
52
+ const components = new Map();
53
+ const pins = [];
54
+ for (const surface of restatedSurfaces(featPlain)) {
55
+ // A malformed current entry reads back as absent (readBaselineRecord drops
56
+ // it into `problems`), so this run reports `pinned` and overwrites it —
57
+ // rebase is the command the gate's `openapi.baseline-invalid` sends people
58
+ // to, and it must repair what it names.
59
+ const from = entryFor(current, surface);
60
+ const base = {
61
+ kind: surface.kind,
62
+ target: surface.kind === "path-item" ? `${surface.path} '${surface.key}'` : surface.id,
63
+ };
64
+ const livingValue = surfaceIn(livingPlain, surface);
65
+ if (!livingValue.found) {
66
+ pins.push({ ...base, status: "unresolved", from: from ?? null, to: null });
67
+ continue;
68
+ }
69
+ const digest = valueDigest(livingValue.value);
70
+ if (surface.kind === "path-item") {
71
+ let keys = pathItems.get(surface.path);
72
+ if (keys === undefined)
73
+ pathItems.set(surface.path, (keys = new Map()));
74
+ keys.set(surface.key, digest);
75
+ }
76
+ else {
77
+ components.set(surface.id, digest);
78
+ }
79
+ pins.push({
80
+ ...base,
81
+ status: from === digest ? "unchanged" : from === undefined ? "pinned" : "repinned",
82
+ from: from ?? null,
83
+ to: digest,
84
+ });
85
+ }
86
+ // The written form drops empty sections, and the whole key when both are —
87
+ // a bare `x-loam-baselines: {}` would be bookkeeping about nothing, in every
88
+ // delta whose surfaces are all new.
89
+ const rebuilt = buildRecord({ pathItems, components });
90
+ const written = {};
91
+ if (Object.keys(rebuilt.pathItems).length > 0)
92
+ written["pathItems"] = rebuilt.pathItems;
93
+ if (Object.keys(rebuilt.components).length > 0)
94
+ written["components"] = rebuilt.components;
95
+ const raw = isRecord(featPlain) ? featPlain[OPENAPI_BASELINES_KEY] : undefined;
96
+ if (Object.keys(written).length === 0) {
97
+ if (raw === undefined)
98
+ return { text: null, pins };
99
+ doc.deleteIn([OPENAPI_BASELINES_KEY]);
100
+ return { text: doc.toString(), pins };
101
+ }
102
+ // Deep-equal, not byte-equal: the comparison must ignore key order exactly as
103
+ // the digests do, so a record an author reordered by hand is left in their
104
+ // order — and a second run over this function's own sorted output finds the
105
+ // parse equal and writes nothing, which is the byte-for-byte idempotence
106
+ // `loam rebase` promises.
107
+ if (isDeepStrictEqual(raw, written))
108
+ return { text: null, pins };
109
+ doc.setIn([OPENAPI_BASELINES_KEY], written);
110
+ return { text: doc.toString(), pins };
111
+ }
112
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/core/openapi/baseline/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What `loam rebase` writes on the surface axis: the `x-loam-baselines` record,\n * rebuilt wholesale from what is on disk right now.\n *\n * The record is rebuilt rather than patched for the same reason the operation\n * pin is always the LIVING digest: a pin is a claim about what its author read,\n * and an entry left behind for a surface the delta no longer restates — or one\n * whose living counterpart is gone — is a claim nobody is making any more.\n * Wholesale rebuild prunes both without a second code path, and the sorted\n * serialization (`buildRecord`) is what makes a second run byte-identical.\n *\n * This module must not import from `../merge/` — the merge reads\n * `./record.ts`, so a value edge back into `merge/` would be a package cycle.\n * That is also why an unreadable document is answered with \"nothing to pin\"\n * instead of `OpenapiMergeError`: every caller chains this AFTER\n * `pinOpenapiOperations`, which has already thrown that error for any document\n * either side cannot parse, so the silent answer here is unreachable in\n * practice and merely keeps the failure ownership where it already lives.\n */\nimport { isDeepStrictEqual } from \"node:util\";\nimport { parseDocument } from \"yaml\";\nimport { isRecord } from \"../../kernel/records.js\";\nimport { OPENAPI_BASELINES_KEY, valueDigest } from \"../digest.js\";\nimport { buildRecord, entryFor, readBaselineRecord, restatedSurfaces, surfaceIn } from \"./record.js\";\n\n/** What happened to one restated surface's baseline entry. */\nexport interface SurfacePin {\n /** Which surface family the entry pins. */\n kind: \"path-item\" | \"component\";\n /** `/orders '<key>'` for a path-item key; `<kind>/<name>` for a component. */\n target: string;\n status:\n /** It had no entry and now has one. */\n | \"pinned\"\n /** It named an older living version; it now names the current one. */\n | \"repinned\"\n /** It already named the current living version — no write. */\n | \"unchanged\"\n /** The living contract has no such surface: this feature is adding it, and it gets NO entry. */\n | \"unresolved\";\n from: string | null;\n to: string | null;\n}\n\nexport interface OpenapiBaselinePlan {\n /** The rewritten contract, or null when the record already says what this run would write. */\n text: string | null;\n pins: SurfacePin[];\n}\n\n/**\n * Pin every restated path-item key and component of a feature contract against\n * the living one. The entry is always `valueDigest` of the LIVING value, never\n * of the delta's own — the one rule that yields both merge verdicts: a surface\n * the author only QUOTED equals its own entry, one they EDITED differs from it.\n * A surface with no living counterpart is `unresolved` and deliberately absent\n * from the record: there is no baseline to claim, and inventing one would make\n * `classifyBaselineDigests` call a genuinely new value `unfounded`.\n */\nexport function planOpenapiBaselines(\n featureText: string,\n livingText: string,\n _service: string,\n): OpenapiBaselinePlan {\n const doc = parseDocument(featureText);\n if (doc.errors.length > 0) return { text: null, pins: [] };\n let featPlain: unknown;\n let livingPlain: unknown;\n try {\n featPlain = doc.toJS() ?? {};\n const living = parseDocument(livingText);\n if (living.errors.length > 0) return { text: null, pins: [] };\n livingPlain = living.toJS() ?? {};\n } catch {\n return { text: null, pins: [] };\n }\n\n const current = readBaselineRecord(featPlain).record;\n const pathItems = new Map<string, Map<string, string>>();\n const components = new Map<string, string>();\n const pins: SurfacePin[] = [];\n for (const surface of restatedSurfaces(featPlain)) {\n // A malformed current entry reads back as absent (readBaselineRecord drops\n // it into `problems`), so this run reports `pinned` and overwrites it —\n // rebase is the command the gate's `openapi.baseline-invalid` sends people\n // to, and it must repair what it names.\n const from = entryFor(current, surface);\n const base = {\n kind: surface.kind,\n target: surface.kind === \"path-item\" ? `${surface.path} '${surface.key}'` : surface.id,\n } as const;\n const livingValue = surfaceIn(livingPlain, surface);\n if (!livingValue.found) {\n pins.push({ ...base, status: \"unresolved\", from: from ?? null, to: null });\n continue;\n }\n const digest = valueDigest(livingValue.value);\n if (surface.kind === \"path-item\") {\n let keys = pathItems.get(surface.path);\n if (keys === undefined) pathItems.set(surface.path, (keys = new Map()));\n keys.set(surface.key, digest);\n } else {\n components.set(surface.id, digest);\n }\n pins.push({\n ...base,\n status: from === digest ? \"unchanged\" : from === undefined ? \"pinned\" : \"repinned\",\n from: from ?? null,\n to: digest,\n });\n }\n\n // The written form drops empty sections, and the whole key when both are —\n // a bare `x-loam-baselines: {}` would be bookkeeping about nothing, in every\n // delta whose surfaces are all new.\n const rebuilt = buildRecord({ pathItems, components });\n const written: Record<string, unknown> = {};\n if (Object.keys(rebuilt.pathItems).length > 0) written[\"pathItems\"] = rebuilt.pathItems;\n if (Object.keys(rebuilt.components).length > 0) written[\"components\"] = rebuilt.components;\n const raw = isRecord(featPlain) ? featPlain[OPENAPI_BASELINES_KEY] : undefined;\n if (Object.keys(written).length === 0) {\n if (raw === undefined) return { text: null, pins };\n doc.deleteIn([OPENAPI_BASELINES_KEY]);\n return { text: doc.toString(), pins };\n }\n // Deep-equal, not byte-equal: the comparison must ignore key order exactly as\n // the digests do, so a record an author reordered by hand is left in their\n // order — and a second run over this function's own sorted output finds the\n // parse equal and writes nothing, which is the byte-for-byte idempotence\n // `loam rebase` promises.\n if (isDeepStrictEqual(raw, written)) return { text: null, pins };\n doc.setIn([OPENAPI_BASELINES_KEY], written);\n return { text: doc.toString(), pins };\n}\n"]}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * The `x-loam-baselines` record: what it holds, how it reads back, and which
3
+ * of a feature document's surfaces it may speak for.
4
+ *
5
+ * One enumeration (`restatedSurfaces`) shared by the rebase plan, the gate and
6
+ * the merge, so the three cannot drift into different ideas of what a feature
7
+ * "restates" — the exact drift the operation axis's shared classifier exists
8
+ * to prevent. Reading is deliberately permissive-with-problems: a malformed
9
+ * record is DESCRIBED (`problems`), never diagnosed here — the gate refuses
10
+ * it, parsers never print, which is doc.ts's own doctrine.
11
+ */
12
+ import { isRecord } from "../../kernel/records.js";
13
+ import { HTTP_METHODS } from "../doc.js";
14
+ import { FEATURE_ONLY_KEYS, OPENAPI_BASELINES_KEY, OPERATION_DIGEST_RE } from "../digest.js";
15
+ export const EMPTY_RECORD = { pathItems: {}, components: {} };
16
+ /** The record a plain-parsed feature document carries, plus everything wrong with it. */
17
+ export function readBaselineRecord(plain) {
18
+ const problems = [];
19
+ const record = { pathItems: {}, components: {} };
20
+ if (!isRecord(plain))
21
+ return { record, problems };
22
+ const raw = plain[OPENAPI_BASELINES_KEY];
23
+ if (raw === undefined)
24
+ return { record, problems };
25
+ if (!isRecord(raw)) {
26
+ problems.push(`${OPENAPI_BASELINES_KEY} is not a mapping`);
27
+ return { record, problems };
28
+ }
29
+ const items = raw["pathItems"];
30
+ if (items !== undefined) {
31
+ if (!isRecord(items))
32
+ problems.push(`${OPENAPI_BASELINES_KEY}.pathItems is not a mapping`);
33
+ else {
34
+ for (const [path, keys] of Object.entries(items)) {
35
+ if (!isRecord(keys)) {
36
+ problems.push(`${OPENAPI_BASELINES_KEY}.pathItems['${path}'] is not a mapping`);
37
+ continue;
38
+ }
39
+ for (const [key, digest] of Object.entries(keys)) {
40
+ if (typeof digest !== "string" || !OPERATION_DIGEST_RE.test(digest)) {
41
+ problems.push(`${OPENAPI_BASELINES_KEY}.pathItems['${path}']['${key}'] is not a 16-hex digest`);
42
+ continue;
43
+ }
44
+ (record.pathItems[path] ??= {})[key] = digest;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ const comps = raw["components"];
50
+ if (comps !== undefined) {
51
+ if (!isRecord(comps))
52
+ problems.push(`${OPENAPI_BASELINES_KEY}.components is not a mapping`);
53
+ else {
54
+ for (const [id, digest] of Object.entries(comps)) {
55
+ if (typeof digest !== "string" || !OPERATION_DIGEST_RE.test(digest)) {
56
+ problems.push(`${OPENAPI_BASELINES_KEY}.components['${id}'] is not a 16-hex digest`);
57
+ continue;
58
+ }
59
+ record.components[id] = digest;
60
+ }
61
+ }
62
+ }
63
+ for (const key of Object.keys(raw)) {
64
+ if (key !== "pathItems" && key !== "components")
65
+ problems.push(`${OPENAPI_BASELINES_KEY}.${key} is not a recognised section`);
66
+ }
67
+ return { record, problems };
68
+ }
69
+ /**
70
+ * Every surface this feature document RESTATES — the enumeration the plan
71
+ * pins, the gate grades and the merge classifies. Path-item surfaces are the
72
+ * non-HTTP-method keys (the feature-only markers are bookkeeping, not
73
+ * surfaces; a METHOD whose value is a removal marker is the operation pin's
74
+ * territory — but a non-method key is a surface whatever its value looks
75
+ * like, because skipping the removal SHAPE here once let it through unpinned
76
+ * and the merge wrote `{x-loam-remove: true}` over a living shared
77
+ * `parameters` at exit 0). Component surfaces are every
78
+ * `components/<kind>/<name>` the document declares.
79
+ */
80
+ export function restatedSurfaces(plain) {
81
+ const out = [];
82
+ if (!isRecord(plain))
83
+ return out;
84
+ const paths = plain["paths"];
85
+ if (isRecord(paths)) {
86
+ for (const [path, item] of Object.entries(paths)) {
87
+ if (!isRecord(item))
88
+ continue;
89
+ for (const [key, value] of Object.entries(item)) {
90
+ // Methods are the operation pin's territory and the feature-only
91
+ // markers are bookkeeping; everything else is a surface — INCLUDING a
92
+ // non-method key whose value happens to be removal-shaped. Skipping
93
+ // that shape here once let it through unpinned and ungraded, and the
94
+ // merge then wrote `{x-loam-remove: true}` over a living shared
95
+ // `parameters` array at exit 0. The merge's own publishable filter
96
+ // (markers.ts withoutFeatureMarkers) must keep agreeing with this
97
+ // pair of exclusions — the surfaces test pins the agreement.
98
+ if (HTTP_METHODS.has(key) || FEATURE_ONLY_KEYS.has(key))
99
+ continue;
100
+ out.push({ kind: "path-item", path, key, value });
101
+ }
102
+ }
103
+ }
104
+ const comps = plain["components"];
105
+ if (isRecord(comps)) {
106
+ for (const [kind, names] of Object.entries(comps)) {
107
+ if (!isRecord(names))
108
+ continue;
109
+ for (const [name, value] of Object.entries(names)) {
110
+ out.push({ kind: "component", id: `${kind}/${name}`, value });
111
+ }
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ /**
117
+ * The value one plain-parsed document holds for one surface, `found`
118
+ * distinguishing "absent" from a legal null. ONE spelling for the plan and the
119
+ * gate, because "does the living contract have this surface" is half of every
120
+ * verdict — two lookups could disagree about absence, and the disagreement
121
+ * would surface as a pin graded against the wrong baseline.
122
+ */
123
+ /**
124
+ * The record's entry for one surface, or undefined — the mirror of
125
+ * `surfaceIn`, and one spelling for the same reason: four open-coded lookups
126
+ * could disagree about absence, and a disagreement surfaces as a pin graded
127
+ * against the wrong baseline.
128
+ */
129
+ export function entryFor(record, surface) {
130
+ return surface.kind === "path-item" ? record.pathItems[surface.path]?.[surface.key] : record.components[surface.id];
131
+ }
132
+ export function surfaceIn(plain, surface) {
133
+ const absent = { found: false, value: undefined };
134
+ if (!isRecord(plain))
135
+ return absent;
136
+ if (surface.kind === "path-item") {
137
+ const paths = plain["paths"];
138
+ if (!isRecord(paths))
139
+ return absent;
140
+ const item = paths[surface.path];
141
+ if (!isRecord(item) || !(surface.key in item))
142
+ return absent;
143
+ return { found: true, value: item[surface.key] };
144
+ }
145
+ // The first `/` is the kind boundary, exactly as the merge's `#/components/`
146
+ // regex reads it — OpenAPI component names cannot legally contain one.
147
+ const cut = surface.id.indexOf("/");
148
+ const comps = plain["components"];
149
+ if (!isRecord(comps))
150
+ return absent;
151
+ const names = comps[surface.id.slice(0, cut)];
152
+ const name = surface.id.slice(cut + 1);
153
+ if (!isRecord(names) || !(name in names))
154
+ return absent;
155
+ return { found: true, value: names[name] };
156
+ }
157
+ /**
158
+ * The record as rebase writes it: lexicographically sorted at every level, so
159
+ * a second run is byte-identical and diffs stay readable.
160
+ */
161
+ export function buildRecord(pins) {
162
+ const sorted = (keys) => [...keys].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
163
+ const record = { pathItems: {}, components: {} };
164
+ for (const path of sorted(pins.pathItems.keys())) {
165
+ const keys = pins.pathItems.get(path);
166
+ const entry = {};
167
+ for (const key of sorted(keys.keys()))
168
+ entry[key] = keys.get(key);
169
+ record.pathItems[path] = entry;
170
+ }
171
+ for (const id of sorted(pins.components.keys()))
172
+ record.components[id] = pins.components.get(id);
173
+ return record;
174
+ }
175
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../../../src/core/openapi/baseline/record.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `x-loam-baselines` record: what it holds, how it reads back, and which\n * of a feature document's surfaces it may speak for.\n *\n * One enumeration (`restatedSurfaces`) shared by the rebase plan, the gate and\n * the merge, so the three cannot drift into different ideas of what a feature\n * \"restates\" — the exact drift the operation axis's shared classifier exists\n * to prevent. Reading is deliberately permissive-with-problems: a malformed\n * record is DESCRIBED (`problems`), never diagnosed here — the gate refuses\n * it, parsers never print, which is doc.ts's own doctrine.\n */\nimport { isRecord } from \"../../kernel/records.js\";\nimport { HTTP_METHODS } from \"../doc.js\";\nimport { FEATURE_ONLY_KEYS, OPENAPI_BASELINES_KEY, OPERATION_DIGEST_RE } from \"../digest.js\";\n\n/** The record as read: per-(path, key) digests and per-(kind/name) digests. */\nexport interface BaselineRecord {\n pathItems: Record<string, Record<string, string>>;\n components: Record<string, string>;\n}\n\nexport const EMPTY_RECORD: BaselineRecord = { pathItems: {}, components: {} };\n\n/** One surface a feature restates: a path-item non-method key, or a component. */\nexport type RestatedSurface =\n | { kind: \"path-item\"; path: string; key: string; value: unknown }\n | { kind: \"component\"; id: string; value: unknown };\n\n/** The record a plain-parsed feature document carries, plus everything wrong with it. */\nexport function readBaselineRecord(plain: unknown): { record: BaselineRecord; problems: string[] } {\n const problems: string[] = [];\n const record: BaselineRecord = { pathItems: {}, components: {} };\n if (!isRecord(plain)) return { record, problems };\n const raw = plain[OPENAPI_BASELINES_KEY];\n if (raw === undefined) return { record, problems };\n if (!isRecord(raw)) {\n problems.push(`${OPENAPI_BASELINES_KEY} is not a mapping`);\n return { record, problems };\n }\n const items = raw[\"pathItems\"];\n if (items !== undefined) {\n if (!isRecord(items)) problems.push(`${OPENAPI_BASELINES_KEY}.pathItems is not a mapping`);\n else {\n for (const [path, keys] of Object.entries(items)) {\n if (!isRecord(keys)) {\n problems.push(`${OPENAPI_BASELINES_KEY}.pathItems['${path}'] is not a mapping`);\n continue;\n }\n for (const [key, digest] of Object.entries(keys)) {\n if (typeof digest !== \"string\" || !OPERATION_DIGEST_RE.test(digest)) {\n problems.push(`${OPENAPI_BASELINES_KEY}.pathItems['${path}']['${key}'] is not a 16-hex digest`);\n continue;\n }\n (record.pathItems[path] ??= {})[key] = digest;\n }\n }\n }\n }\n const comps = raw[\"components\"];\n if (comps !== undefined) {\n if (!isRecord(comps)) problems.push(`${OPENAPI_BASELINES_KEY}.components is not a mapping`);\n else {\n for (const [id, digest] of Object.entries(comps)) {\n if (typeof digest !== \"string\" || !OPERATION_DIGEST_RE.test(digest)) {\n problems.push(`${OPENAPI_BASELINES_KEY}.components['${id}'] is not a 16-hex digest`);\n continue;\n }\n record.components[id] = digest;\n }\n }\n }\n for (const key of Object.keys(raw)) {\n if (key !== \"pathItems\" && key !== \"components\") problems.push(`${OPENAPI_BASELINES_KEY}.${key} is not a recognised section`);\n }\n return { record, problems };\n}\n\n/**\n * Every surface this feature document RESTATES — the enumeration the plan\n * pins, the gate grades and the merge classifies. Path-item surfaces are the\n * non-HTTP-method keys (the feature-only markers are bookkeeping, not\n * surfaces; a METHOD whose value is a removal marker is the operation pin's\n * territory — but a non-method key is a surface whatever its value looks\n * like, because skipping the removal SHAPE here once let it through unpinned\n * and the merge wrote `{x-loam-remove: true}` over a living shared\n * `parameters` at exit 0). Component surfaces are every\n * `components/<kind>/<name>` the document declares.\n */\nexport function restatedSurfaces(plain: unknown): RestatedSurface[] {\n const out: RestatedSurface[] = [];\n if (!isRecord(plain)) return out;\n const paths = plain[\"paths\"];\n if (isRecord(paths)) {\n for (const [path, item] of Object.entries(paths)) {\n if (!isRecord(item)) continue;\n for (const [key, value] of Object.entries(item)) {\n // Methods are the operation pin's territory and the feature-only\n // markers are bookkeeping; everything else is a surface — INCLUDING a\n // non-method key whose value happens to be removal-shaped. Skipping\n // that shape here once let it through unpinned and ungraded, and the\n // merge then wrote `{x-loam-remove: true}` over a living shared\n // `parameters` array at exit 0. The merge's own publishable filter\n // (markers.ts withoutFeatureMarkers) must keep agreeing with this\n // pair of exclusions — the surfaces test pins the agreement.\n if (HTTP_METHODS.has(key) || FEATURE_ONLY_KEYS.has(key)) continue;\n out.push({ kind: \"path-item\", path, key, value });\n }\n }\n }\n const comps = plain[\"components\"];\n if (isRecord(comps)) {\n for (const [kind, names] of Object.entries(comps)) {\n if (!isRecord(names)) continue;\n for (const [name, value] of Object.entries(names)) {\n out.push({ kind: \"component\", id: `${kind}/${name}`, value });\n }\n }\n }\n return out;\n}\n\n/**\n * The value one plain-parsed document holds for one surface, `found`\n * distinguishing \"absent\" from a legal null. ONE spelling for the plan and the\n * gate, because \"does the living contract have this surface\" is half of every\n * verdict — two lookups could disagree about absence, and the disagreement\n * would surface as a pin graded against the wrong baseline.\n */\n/**\n * The record's entry for one surface, or undefined — the mirror of\n * `surfaceIn`, and one spelling for the same reason: four open-coded lookups\n * could disagree about absence, and a disagreement surfaces as a pin graded\n * against the wrong baseline.\n */\nexport function entryFor(record: BaselineRecord, surface: RestatedSurface): string | undefined {\n return surface.kind === \"path-item\" ? record.pathItems[surface.path]?.[surface.key] : record.components[surface.id];\n}\n\nexport function surfaceIn(plain: unknown, surface: RestatedSurface): { found: boolean; value: unknown } {\n const absent = { found: false, value: undefined };\n if (!isRecord(plain)) return absent;\n if (surface.kind === \"path-item\") {\n const paths = plain[\"paths\"];\n if (!isRecord(paths)) return absent;\n const item = paths[surface.path];\n if (!isRecord(item) || !(surface.key in item)) return absent;\n return { found: true, value: item[surface.key] };\n }\n // The first `/` is the kind boundary, exactly as the merge's `#/components/`\n // regex reads it — OpenAPI component names cannot legally contain one.\n const cut = surface.id.indexOf(\"/\");\n const comps = plain[\"components\"];\n if (!isRecord(comps)) return absent;\n const names = comps[surface.id.slice(0, cut)];\n const name = surface.id.slice(cut + 1);\n if (!isRecord(names) || !(name in names)) return absent;\n return { found: true, value: names[name] };\n}\n\n/**\n * The record as rebase writes it: lexicographically sorted at every level, so\n * a second run is byte-identical and diffs stay readable.\n */\nexport function buildRecord(pins: { pathItems: Map<string, Map<string, string>>; components: Map<string, string> }): BaselineRecord {\n const sorted = (keys: Iterable<string>): string[] => [...keys].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n const record: BaselineRecord = { pathItems: {}, components: {} };\n for (const path of sorted(pins.pathItems.keys())) {\n const keys = pins.pathItems.get(path)!;\n const entry: Record<string, string> = {};\n for (const key of sorted(keys.keys())) entry[key] = keys.get(key)!;\n record.pathItems[path] = entry;\n }\n for (const id of sorted(pins.components.keys())) record.components[id] = pins.components.get(id)!;\n return record;\n}\n"]}