@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,245 @@
1
+ /**
2
+ * What each axis is missing a pin for, computed before anything is written.
3
+ *
4
+ * The requirement axis and the contract axis are planned by the same shape and
5
+ * separately on purpose: a requirement is pinned by a `Based-On:` line inserted
6
+ * by surgery, and an operation by an `x-loam-based-on` key the YAML writer
7
+ * emits. Only the first has to preserve the author's sections and prose, which
8
+ * is why it edits lines rather than reserializing.
9
+ */
10
+ import { existsSync } from "node:fs";
11
+ import { readFile } from "node:fs/promises";
12
+ import { decodeDocument } from "../../core/kernel/document-bytes.js";
13
+ import {} from "../../core/kernel/ids/service.js";
14
+ import {} from "../../core/repo/paths.js";
15
+ import { livingCapabilityPaths } from "../../core/repo/authored/paths.js";
16
+ import { locateServicePaths } from "../../core/repo/service-target.js";
17
+ import { parseRequirements, readRequirementsDocument } from "../../core/document/parse.js";
18
+ import { requirementDigest } from "../../core/document/spec.js";
19
+ import { OpenapiMergeError } from "../../core/openapi/merge/error.js";
20
+ import { pinOpenapiOperations } from "../../core/openapi/merge/pin.js";
21
+ import { planOpenapiBaselines } from "../../core/openapi/baseline/plan.js";
22
+ import { AsyncapiMergeError } from "../../core/asyncapi/merge/error.js";
23
+ import { pinAsyncapiSlots } from "../../core/asyncapi/merge/pin.js";
24
+ import { planAsyncapiBaselines } from "../../core/asyncapi/baseline/plan.js";
25
+ import { applyEdits, pinEdit } from "./edit.js";
26
+ /** Pin every MODIFIED/REMOVED requirement in one delta file against one living document. */
27
+ export async function planAxis(repo, service, axis, specPath) {
28
+ const livingPath = (await locateServicePaths(repo.docsDir, service, repo.fleet))[axis.key];
29
+ const planned = await planRequirementPins(specPath, livingPath, axis.file);
30
+ return { outcomes: planned.pins.map((pin) => ({ service, ...pin })), content: planned.content };
31
+ }
32
+ /**
33
+ * The same, for one capability delta against its living document.
34
+ *
35
+ * NOT OPTIONAL, and not symmetry for its own sake: `delta.baseline-missing` is
36
+ * a warning that GATES archive, and its message ends "Run `loam rebase <FEAT>`
37
+ * to pin it". A capability delta graded by that rule while `rebase` skipped the
38
+ * axis would have loam send an author to a command that does not pin the file
39
+ * loam just named — a refusal with no exit, which is the one thing a gating
40
+ * message may never be.
41
+ *
42
+ * The living path resolves through `livingCapabilityPaths` for the reason that
43
+ * function states: the id's nesting is spelled by directories, so an unsplit
44
+ * join reads an empty living document and every pin comes out `unresolved`
45
+ * over a promise the fleet already made.
46
+ */
47
+ export async function planCapabilityAxis(repo, capability, specPath) {
48
+ const planned = await planRequirementPins(specPath, livingCapabilityPaths(repo.docsDir, capability).spec, "spec.md");
49
+ return { outcomes: planned.pins.map((pin) => ({ capability, ...pin })), content: planned.content };
50
+ }
51
+ /**
52
+ * The pin rule itself: every MODIFIED/REMOVED requirement in one delta document,
53
+ * against one living document. Whose documents they are is the caller's
54
+ * business — this is the part that must not fork, because a second copy is a
55
+ * second answer to "was this delta written against what is there now".
56
+ */
57
+ async function planRequirementPins(specPath, livingPath, file) {
58
+ // Both sides through the decoding read: the delta because line surgery over a
59
+ // document loam mis-decoded would write bytes nobody authored, the living
60
+ // because it is what every digest below is taken over.
61
+ const raw = await readRequirementsDocument(specPath);
62
+ const reqs = parseRequirements(raw);
63
+ const living = existsSync(livingPath)
64
+ ? parseRequirements(await readRequirementsDocument(livingPath))
65
+ : [];
66
+ const pins = [];
67
+ const edits = [];
68
+ for (const r of reqs) {
69
+ if (r.kind !== "MODIFIED" && r.kind !== "REMOVED")
70
+ continue;
71
+ // Every requirement parsed from a document carries its heading line
72
+ // (core/document/parse.ts). One built in memory has no document to write into, and
73
+ // there is nothing truthful to report about pinning it.
74
+ const line = r.line;
75
+ if (line === undefined)
76
+ continue;
77
+ const base = { file, kind: r.kind, target: r.name };
78
+ const selected = selectLiving(living, r);
79
+ if (selected === undefined) {
80
+ pins.push({ ...base, status: "unresolved", from: r.basedOn ?? null, to: null });
81
+ continue;
82
+ }
83
+ const digest = requirementDigest(selected);
84
+ if (r.basedOn === digest) {
85
+ pins.push({ ...base, status: "unchanged", from: digest, to: digest });
86
+ continue;
87
+ }
88
+ pins.push({
89
+ ...base,
90
+ status: r.basedOn === undefined ? "pinned" : "repinned",
91
+ from: r.basedOn ?? null,
92
+ to: digest,
93
+ });
94
+ edits.push(pinEdit(r, line, digest));
95
+ }
96
+ return { pins, content: edits.length === 0 ? null : applyEdits(raw, edits) };
97
+ }
98
+ /**
99
+ * Pin every operation in one feature contract against the living contract.
100
+ *
101
+ * The pin is always `operationDigest` of the LIVING operation, never of the
102
+ * delta's own. That single rule produces both merge verdicts on its own: an
103
+ * operation the author only QUOTED is byte-equal to living, so its pin equals
104
+ * its own content and the merge skips it; one the author EDITED differs from
105
+ * its pin, so the merge writes it. Nothing here has to guess at intent — the
106
+ * document already records it, and the pin makes it legible.
107
+ *
108
+ * Slot-keyed (path + method), exactly as the merge upserts. An operationId that
109
+ * moved to another slot is a new slot with nothing living behind it, which is
110
+ * `unresolved` and correct: there is no living version of an operation at a
111
+ * path the contract does not serve yet.
112
+ */
113
+ export async function planOpenapi(repo, service, openapiPath) {
114
+ const livingPath = (await locateServicePaths(repo.docsDir, service, repo.fleet)).openapi;
115
+ // Decoded, not `readFile(…, "utf8")`, for the requirement axes' reason: a
116
+ // contract read with U+FFFD substituted in defines no operation loam can
117
+ // match, so every pin would come out `unresolved` — "this feature adds them
118
+ // all" — over a living contract that already serves them. Read outside the
119
+ // try below, whose job is the merge's own diagnosis and not this one.
120
+ const living = existsSync(livingPath) ? decodeDocument(await readFile(livingPath), livingPath) : "";
121
+ const delta = decodeDocument(await readFile(openapiPath), openapiPath);
122
+ let plan;
123
+ let baselines;
124
+ try {
125
+ plan = pinOpenapiOperations(delta, living, service);
126
+ // The surface record, planned over the OPERATION pass's output so one run
127
+ // writes one file once. Order is safe: a surface digest never includes an
128
+ // operation pin (`restatedSurfaces` skips the methods entirely), and the
129
+ // root record never enters an operation digest.
130
+ baselines = planOpenapiBaselines(plan.text ?? delta, living, service);
131
+ }
132
+ catch (err) {
133
+ // A document loam cannot read is validate's diagnosis to make
134
+ // (`openapi.invalid`), not this command's to guess at — and stamping into a
135
+ // broken parse would write a file nobody asked for.
136
+ if (!(err instanceof OpenapiMergeError))
137
+ throw err;
138
+ return { outcomes: [], content: null };
139
+ }
140
+ return {
141
+ outcomes: [
142
+ ...plan.pins.map((pin) => ({
143
+ service,
144
+ file: "openapi.yaml",
145
+ kind: pin.method.toUpperCase(),
146
+ target: pin.operationId.length === 0 ? pin.path : `${pin.path} ('${pin.operationId}')`,
147
+ status: pin.status,
148
+ from: pin.from,
149
+ to: pin.to,
150
+ })),
151
+ ...baselines.pins.map((pin) => ({
152
+ service,
153
+ file: "openapi.yaml",
154
+ kind: pin.kind === "component" ? "COMPONENT" : "PATH-ITEM",
155
+ target: pin.target,
156
+ status: pin.status,
157
+ from: pin.from,
158
+ to: pin.to,
159
+ })),
160
+ ],
161
+ content: baselines.text ?? plan.text,
162
+ };
163
+ }
164
+ /**
165
+ * Pin every slot AND every component surface in one feature event contract
166
+ * against the living contract — `planOpenapi`'s mirror on the event axis, in
167
+ * both of its halves.
168
+ *
169
+ * The in-value `x-loam-based-on` reaches every SLOT, because a slot value is
170
+ * always a mapping (core/asyncapi/digest.ts owns the slot model; inline
171
+ * channel messages are channel interior and never pinned on their own). It
172
+ * reaches nothing else: a `components/schemas/<name>` value is a JSON Schema,
173
+ * where an in-value loam key would be a schema keyword — so the surfaces are
174
+ * pinned by the same root `x-loam-baselines` record the OpenAPI axis uses, and
175
+ * this function writes both. (This comment used to say the event pin has no
176
+ * root record. It did not, and that is why a `components.schemas`-only delta
177
+ * merged nothing at all.)
178
+ */
179
+ export async function planAsyncapi(repo, service, asyncapiPath) {
180
+ const livingPath = (await locateServicePaths(repo.docsDir, service, repo.fleet)).asyncapi;
181
+ // Decoded, not `readFile(…, "utf8")`, for planOpenapi's reason: a contract
182
+ // read with U+FFFD substituted in defines no slot loam can match, so every
183
+ // pin would come out `unresolved` over a living contract that already
184
+ // carries them.
185
+ const living = existsSync(livingPath) ? decodeDocument(await readFile(livingPath), livingPath) : "";
186
+ const delta = decodeDocument(await readFile(asyncapiPath), asyncapiPath);
187
+ let plan;
188
+ let baselines;
189
+ try {
190
+ plan = pinAsyncapiSlots(delta, living, service);
191
+ // The surface record, planned over the SLOT pass's output so one run
192
+ // writes one file once. Order is safe, and load-bearing in both
193
+ // directions: a surface digest never includes a slot pin (`asyncapiSurfaces`
194
+ // skips `components.messages` entirely, and no other section is a
195
+ // surface), and the root record never enters a slot digest.
196
+ baselines = planAsyncapiBaselines(plan.text ?? delta, living, service);
197
+ }
198
+ catch (err) {
199
+ // A document loam cannot read is validate's diagnosis to make
200
+ // (`asyncapi.invalid`), not this command's to guess at — and stamping
201
+ // into a broken parse would write a file nobody asked for.
202
+ if (!(err instanceof AsyncapiMergeError))
203
+ throw err;
204
+ return { outcomes: [], content: null };
205
+ }
206
+ return {
207
+ outcomes: [
208
+ ...plan.pins.map((pin) => ({
209
+ service,
210
+ file: "asyncapi.yaml",
211
+ kind: pin.section,
212
+ target: pin.key,
213
+ status: pin.status,
214
+ from: pin.from,
215
+ to: pin.to,
216
+ })),
217
+ ...baselines.pins.map((pin) => ({
218
+ service,
219
+ file: "asyncapi.yaml",
220
+ kind: "COMPONENT",
221
+ target: pin.target,
222
+ status: pin.status,
223
+ from: pin.from,
224
+ to: pin.to,
225
+ })),
226
+ ],
227
+ content: baselines.text ?? plan.text,
228
+ };
229
+ }
230
+ /**
231
+ * The living requirement a delta requirement addresses — the merge's own
232
+ * resolution order (core/document/apply.ts `applyRequirementDelta`), so a pin is taken
233
+ * over exactly the requirement archive would rewrite: the stable ID when the
234
+ * delta carries one, the exact heading otherwise, and the FIRST match either
235
+ * way (duplicates are refused upstream by `delta.living-duplicate-requirement`).
236
+ */
237
+ export function selectLiving(living, r) {
238
+ if (r.id !== undefined)
239
+ return living.find((candidate) => candidate.id === r.id);
240
+ return living.find((candidate) => candidate.name === r.name);
241
+ }
242
+ /* ------------------------------------------------------------------ */
243
+ /* Line surgery */
244
+ /* ------------------------------------------------------------------ */
245
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/rebase/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What each axis is missing a pin for, computed before anything is written.\n *\n * The requirement axis and the contract axis are planned by the same shape and\n * separately on purpose: a requirement is pinned by a `Based-On:` line inserted\n * by surgery, and an operation by an `x-loam-based-on` key the YAML writer\n * emits. Only the first has to preserve the author's sections and prose, which\n * is why it edits lines rather than reserializing.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { decodeDocument } from \"../../core/kernel/document-bytes.js\";\nimport { type PathableService } from \"../../core/kernel/ids/service.js\";\nimport { type SpecAxis } from \"../../core/repo/paths.js\";\nimport { livingCapabilityPaths } from \"../../core/repo/authored/paths.js\";\nimport { locateServicePaths } from \"../../core/repo/service-target.js\";\nimport { parseRequirements, readRequirementsDocument } from \"../../core/document/parse.js\";\nimport { requirementDigest, type Requirement } from \"../../core/document/spec.js\";\nimport { OpenapiMergeError } from \"../../core/openapi/merge/error.js\";\nimport { pinOpenapiOperations, type OpenapiPinPlan } from \"../../core/openapi/merge/pin.js\";\nimport { planOpenapiBaselines, type OpenapiBaselinePlan } from \"../../core/openapi/baseline/plan.js\";\nimport { AsyncapiMergeError } from \"../../core/asyncapi/merge/error.js\";\nimport { pinAsyncapiSlots, type AsyncapiPinPlan } from \"../../core/asyncapi/merge/pin.js\";\nimport { planAsyncapiBaselines, type AsyncapiBaselinePlan } from \"../../core/asyncapi/baseline/plan.js\";\nimport { applyEdits, pinEdit, type LineEdit } from \"./edit.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport type { FleetContext } from \"../../core/fleet-context.js\";\n\n/**\n * How a planner reaches the living documents: the repo root plus the\n * command's shared read index. One record on purpose — `rebase` calls these\n * planners in a services × axes loop, and each `locateServicePaths` without\n * a context is a FULL fleet walk (readdir + a frontmatter read per service),\n * so a 20-service feature paid ~80 re-enumerations of a fleet that did not\n * change between them. The context makes that one walk per invocation.\n */\nexport interface RepoRead {\n docsDir: DocsDir;\n fleet?: FleetContext;\n}\n\nexport type PinStatus =\n /** It had none and now has one. */\n | \"pinned\"\n /** It had a pin for 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 /**\n * It addresses nothing living — a MODIFIED of a requirement another feature\n * in flight still introduces, or an operation this feature is adding. There\n * is no version to be based on, so inventing a pin would be inventing a\n * baseline; `loam validate` already reports the ordering where there is one\n * (`delta.modified-pending`).\n */\n | \"unresolved\"\n /**\n * The operation is written as a YAML alias. One shared value backs every use\n * of an anchor, so stamping \"through\" it would pin every other use too —\n * the same reason the merge refuses to edit aliases in place. Rare, legal,\n * and named rather than silently skipped.\n */\n | \"unwritable\";\n\n/**\n * What happened to ONE pin, minus whose document it was in.\n *\n * Split from `PinOutcome` when the business corpus arrived: a capability delta\n * is pinned by the identical rule against an identical document, but it names\n * no service, and putting a capability id into a published field called\n * `service` would bend the `--json` contract into a lie. Everything a reader\n * acts on lives here; the two wrappers below add only the identity.\n */\nexport interface PinRecord {\n /** The axis's filename — \"spec.md\", \"arch.spec.md\", \"openapi.yaml\" or \"asyncapi.yaml\". */\n file: string;\n /**\n * MODIFIED/REMOVED for a requirement; the upper-case HTTP method for an\n * operation; COMPONENT for a `components/<kind>/<name>`; PATH-ITEM for a\n * path-item non-method key; the AsyncAPI section name (`channels`,\n * `operations`, `components.messages`) for an event slot.\n */\n kind: string;\n /** The requirement heading, `/path ('operationId')`, `<kind>/<name>`, `/path '<key>'`, or the asyncapi slot key. */\n target: string;\n status: PinStatus;\n /** The pin as it was, or null when there was none. */\n from: string | null;\n /** The pin as it now stands, or null when nothing living could be hashed. */\n to: string | null;\n}\n\nexport interface PinOutcome extends PinRecord {\n service: string;\n}\n\n/** The same record on the business corpus, keyed by the promise instead of the service. */\nexport interface CapabilityPinOutcome extends PinRecord {\n capability: string;\n}\n\nexport interface AxisPlan {\n outcomes: PinOutcome[];\n /** The rewritten document, or null when nothing about it changes. */\n content: string | null;\n}\n\nexport interface CapabilityAxisPlan {\n outcomes: CapabilityPinOutcome[];\n content: string | null;\n}\n\n/** Pin every MODIFIED/REMOVED requirement in one delta file against one living document. */\nexport async function planAxis(\n repo: RepoRead,\n service: PathableService,\n axis: SpecAxis,\n specPath: string,\n): Promise<AxisPlan> {\n const livingPath = (await locateServicePaths(repo.docsDir, service, repo.fleet))[axis.key];\n const planned = await planRequirementPins(specPath, livingPath, axis.file);\n return { outcomes: planned.pins.map((pin) => ({ service, ...pin })), content: planned.content };\n}\n\n/**\n * The same, for one capability delta against its living document.\n *\n * NOT OPTIONAL, and not symmetry for its own sake: `delta.baseline-missing` is\n * a warning that GATES archive, and its message ends \"Run `loam rebase <FEAT>`\n * to pin it\". A capability delta graded by that rule while `rebase` skipped the\n * axis would have loam send an author to a command that does not pin the file\n * loam just named — a refusal with no exit, which is the one thing a gating\n * message may never be.\n *\n * The living path resolves through `livingCapabilityPaths` for the reason that\n * function states: the id's nesting is spelled by directories, so an unsplit\n * join reads an empty living document and every pin comes out `unresolved`\n * over a promise the fleet already made.\n */\nexport async function planCapabilityAxis(\n repo: RepoRead,\n capability: string,\n specPath: string,\n): Promise<CapabilityAxisPlan> {\n const planned = await planRequirementPins(specPath, livingCapabilityPaths(repo.docsDir, capability).spec, \"spec.md\");\n return { outcomes: planned.pins.map((pin) => ({ capability, ...pin })), content: planned.content };\n}\n\n/**\n * The pin rule itself: every MODIFIED/REMOVED requirement in one delta document,\n * against one living document. Whose documents they are is the caller's\n * business — this is the part that must not fork, because a second copy is a\n * second answer to \"was this delta written against what is there now\".\n */\nasync function planRequirementPins(\n specPath: string,\n livingPath: string,\n file: string,\n): Promise<{ pins: PinRecord[]; content: string | null }> {\n // Both sides through the decoding read: the delta because line surgery over a\n // document loam mis-decoded would write bytes nobody authored, the living\n // because it is what every digest below is taken over.\n const raw = await readRequirementsDocument(specPath);\n const reqs = parseRequirements(raw);\n const living = existsSync(livingPath)\n ? parseRequirements(await readRequirementsDocument(livingPath))\n : [];\n\n const pins: PinRecord[] = [];\n const edits: LineEdit[] = [];\n for (const r of reqs) {\n if (r.kind !== \"MODIFIED\" && r.kind !== \"REMOVED\") continue;\n // Every requirement parsed from a document carries its heading line\n // (core/document/parse.ts). One built in memory has no document to write into, and\n // there is nothing truthful to report about pinning it.\n const line = r.line;\n if (line === undefined) continue;\n const base = { file, kind: r.kind, target: r.name };\n const selected = selectLiving(living, r);\n if (selected === undefined) {\n pins.push({ ...base, status: \"unresolved\", from: r.basedOn ?? null, to: null });\n continue;\n }\n const digest = requirementDigest(selected);\n if (r.basedOn === digest) {\n pins.push({ ...base, status: \"unchanged\", from: digest, to: digest });\n continue;\n }\n pins.push({\n ...base,\n status: r.basedOn === undefined ? \"pinned\" : \"repinned\",\n from: r.basedOn ?? null,\n to: digest,\n });\n edits.push(pinEdit(r, line, digest));\n }\n\n return { pins, content: edits.length === 0 ? null : applyEdits(raw, edits) };\n}\n\n/**\n * Pin every operation in one feature contract against the living contract.\n *\n * The pin is always `operationDigest` of the LIVING operation, never of the\n * delta's own. That single rule produces both merge verdicts on its own: an\n * operation the author only QUOTED is byte-equal to living, so its pin equals\n * its own content and the merge skips it; one the author EDITED differs from\n * its pin, so the merge writes it. Nothing here has to guess at intent — the\n * document already records it, and the pin makes it legible.\n *\n * Slot-keyed (path + method), exactly as the merge upserts. An operationId that\n * moved to another slot is a new slot with nothing living behind it, which is\n * `unresolved` and correct: there is no living version of an operation at a\n * path the contract does not serve yet.\n */\nexport async function planOpenapi(repo: RepoRead, service: PathableService, openapiPath: string): Promise<AxisPlan> {\n const livingPath = (await locateServicePaths(repo.docsDir, service, repo.fleet)).openapi;\n // Decoded, not `readFile(…, \"utf8\")`, for the requirement axes' reason: a\n // contract read with U+FFFD substituted in defines no operation loam can\n // match, so every pin would come out `unresolved` — \"this feature adds them\n // all\" — over a living contract that already serves them. Read outside the\n // try below, whose job is the merge's own diagnosis and not this one.\n const living = existsSync(livingPath) ? decodeDocument(await readFile(livingPath), livingPath) : \"\";\n const delta = decodeDocument(await readFile(openapiPath), openapiPath);\n let plan: OpenapiPinPlan;\n let baselines: OpenapiBaselinePlan;\n try {\n plan = pinOpenapiOperations(delta, living, service);\n // The surface record, planned over the OPERATION pass's output so one run\n // writes one file once. Order is safe: a surface digest never includes an\n // operation pin (`restatedSurfaces` skips the methods entirely), and the\n // root record never enters an operation digest.\n baselines = planOpenapiBaselines(plan.text ?? delta, living, service);\n } catch (err) {\n // A document loam cannot read is validate's diagnosis to make\n // (`openapi.invalid`), not this command's to guess at — and stamping into a\n // broken parse would write a file nobody asked for.\n if (!(err instanceof OpenapiMergeError)) throw err;\n return { outcomes: [], content: null };\n }\n return {\n outcomes: [\n ...plan.pins.map((pin) => ({\n service,\n file: \"openapi.yaml\",\n kind: pin.method.toUpperCase(),\n target: pin.operationId.length === 0 ? pin.path : `${pin.path} ('${pin.operationId}')`,\n status: pin.status,\n from: pin.from,\n to: pin.to,\n })),\n ...baselines.pins.map((pin) => ({\n service,\n file: \"openapi.yaml\",\n kind: pin.kind === \"component\" ? \"COMPONENT\" : \"PATH-ITEM\",\n target: pin.target,\n status: pin.status,\n from: pin.from,\n to: pin.to,\n })),\n ],\n content: baselines.text ?? plan.text,\n };\n}\n\n/**\n * Pin every slot AND every component surface in one feature event contract\n * against the living contract — `planOpenapi`'s mirror on the event axis, in\n * both of its halves.\n *\n * The in-value `x-loam-based-on` reaches every SLOT, because a slot value is\n * always a mapping (core/asyncapi/digest.ts owns the slot model; inline\n * channel messages are channel interior and never pinned on their own). It\n * reaches nothing else: a `components/schemas/<name>` value is a JSON Schema,\n * where an in-value loam key would be a schema keyword — so the surfaces are\n * pinned by the same root `x-loam-baselines` record the OpenAPI axis uses, and\n * this function writes both. (This comment used to say the event pin has no\n * root record. It did not, and that is why a `components.schemas`-only delta\n * merged nothing at all.)\n */\nexport async function planAsyncapi(\n repo: RepoRead,\n service: PathableService,\n asyncapiPath: string,\n): Promise<AxisPlan> {\n const livingPath = (await locateServicePaths(repo.docsDir, service, repo.fleet)).asyncapi;\n // Decoded, not `readFile(…, \"utf8\")`, for planOpenapi's reason: a contract\n // read with U+FFFD substituted in defines no slot loam can match, so every\n // pin would come out `unresolved` over a living contract that already\n // carries them.\n const living = existsSync(livingPath) ? decodeDocument(await readFile(livingPath), livingPath) : \"\";\n const delta = decodeDocument(await readFile(asyncapiPath), asyncapiPath);\n let plan: AsyncapiPinPlan;\n let baselines: AsyncapiBaselinePlan;\n try {\n plan = pinAsyncapiSlots(delta, living, service);\n // The surface record, planned over the SLOT pass's output so one run\n // writes one file once. Order is safe, and load-bearing in both\n // directions: a surface digest never includes a slot pin (`asyncapiSurfaces`\n // skips `components.messages` entirely, and no other section is a\n // surface), and the root record never enters a slot digest.\n baselines = planAsyncapiBaselines(plan.text ?? delta, living, service);\n } catch (err) {\n // A document loam cannot read is validate's diagnosis to make\n // (`asyncapi.invalid`), not this command's to guess at — and stamping\n // into a broken parse would write a file nobody asked for.\n if (!(err instanceof AsyncapiMergeError)) throw err;\n return { outcomes: [], content: null };\n }\n return {\n outcomes: [\n ...plan.pins.map((pin) => ({\n service,\n file: \"asyncapi.yaml\",\n kind: pin.section,\n target: pin.key,\n status: pin.status,\n from: pin.from,\n to: pin.to,\n })),\n ...baselines.pins.map((pin) => ({\n service,\n file: \"asyncapi.yaml\",\n kind: \"COMPONENT\",\n target: pin.target,\n status: pin.status,\n from: pin.from,\n to: pin.to,\n })),\n ],\n content: baselines.text ?? plan.text,\n };\n}\n\n/**\n * The living requirement a delta requirement addresses — the merge's own\n * resolution order (core/document/apply.ts `applyRequirementDelta`), so a pin is taken\n * over exactly the requirement archive would rewrite: the stable ID when the\n * delta carries one, the exact heading otherwise, and the FIRST match either\n * way (duplicates are refused upstream by `delta.living-duplicate-requirement`).\n */\nexport function selectLiving(living: Requirement[], r: Requirement): Requirement | undefined {\n if (r.id !== undefined) return living.find((candidate) => candidate.id === r.id);\n return living.find((candidate) => candidate.name === r.name);\n}\n\n/* ------------------------------------------------------------------ */\n/* Line surgery */\n/* ------------------------------------------------------------------ */\n"]}
@@ -0,0 +1,275 @@
1
+ import { existsSync } from "node:fs";
2
+ import { loadConfig } from "../../core/envelope/config.js";
3
+ import { NotUtf8DocumentError } from "../../core/kernel/document-bytes.js";
4
+ import { InvalidIdError, assertServiceId } from "../../core/kernel/ids/service.js";
5
+ import { emitJson, fail, repoPath, reportNoConfig } from "../../core/envelope/json.js";
6
+ import { compareIds } from "../../core/repo/entries.js";
7
+ import { featureSpecPaths, SPEC_AXES } from "../../core/repo/paths.js";
8
+ import { missingFeatureMessage, resolveFeature } from "../../core/repo/repo.js";
9
+ import { stageWrites } from "../../core/staging/commit.js";
10
+ import { InterruptedCommitError } from "../../core/staging/interrupted.js";
11
+ import { acquireDocsLock, DocsBusyError } from "../../core/staging/lock.js";
12
+ import { recoverInterruptedCommit } from "../../core/staging/recovery/recover.js";
13
+ import { commitStaged } from "../../core/staging/txn/transaction.js";
14
+ import {} from "../../core/staging/writes.js";
15
+ import { plural, sayRecovered } from "../policy/format.js";
16
+ import { FleetContext } from "../../core/fleet-context.js";
17
+ import { planAsyncapi, planAxis, planCapabilityAxis, planOpenapi, } from "./plan.js";
18
+ import { rebaseLivingLocked } from "./living.js";
19
+ /** What happened to one pin. */
20
+ export function registerRebase(program) {
21
+ program
22
+ .command("rebase")
23
+ // Optional ONLY because `--living` names no feature: that mode pins the
24
+ // living corpus itself. Every existing invocation still parses, which is
25
+ // what makes the relaxation additive rather than a contract change.
26
+ .argument("[featureId]", "feature id, e.g. FEAT-101 — omitted with --living")
27
+ .description("Pin a feature's MODIFIED/REMOVED requirements to the living text they are written against")
28
+ .option("--service <id>", "restrict to one service (default: every service the feature touches)")
29
+ .option("--living", "pin the LIVING corpus's `Realizes:` entries to the capability requirements they name, instead of a feature's deltas")
30
+ .option("--dry-run", "print what would be pinned and write nothing — beyond first finishing a predecessor's interrupted commit, exactly as a real run would")
31
+ .option("--json", "emit the machine contract instead of the human view")
32
+ .action(async (featureId, opts) => {
33
+ const json = opts.json === true;
34
+ const loaded = await loadConfig();
35
+ if (loaded.kind !== "loaded") {
36
+ reportNoConfig(json, loaded);
37
+ return;
38
+ }
39
+ const config = loaded.config;
40
+ // The id grammar on the RAW argument, before it reaches a path join —
41
+ // one grammar for the whole tool (core/kernel/ids/service.ts).
42
+ if (opts.service !== undefined) {
43
+ try {
44
+ assertServiceId(opts.service, "--service");
45
+ }
46
+ catch (err) {
47
+ if (!(err instanceof InvalidIdError))
48
+ throw err;
49
+ return fail(json, "invalid-option", err.message);
50
+ }
51
+ }
52
+ // The living specs must not move between the read that computes a pin and
53
+ // the write that records it: a digest taken over a document an archive
54
+ // replaced a millisecond later is precisely the false "I read this" the
55
+ // pin exists to make impossible. One writer per docs repo, refusing
56
+ // rather than queueing, exactly as archive takes it.
57
+ let release;
58
+ try {
59
+ release = await acquireDocsLock(config.docsDir);
60
+ }
61
+ catch (err) {
62
+ if (!(err instanceof DocsBusyError))
63
+ throw err;
64
+ return fail(json, "docs-busy", err.message);
65
+ }
66
+ try {
67
+ // A journal in the repo means the LAST write never finished — reading
68
+ // living documents to compute pins over a half-commit would digest
69
+ // bytes no run ever produced. Recover (or refuse) first, under the
70
+ // same lock the commit will hold.
71
+ let recovered;
72
+ try {
73
+ recovered = await recoverInterruptedCommit(config.docsDir);
74
+ }
75
+ catch (err) {
76
+ if (!(err instanceof InterruptedCommitError))
77
+ throw err;
78
+ return fail(json, "commit-interrupted", err.message);
79
+ }
80
+ if (opts.living === true) {
81
+ await rebaseLivingLocked(config.docsDir, opts);
82
+ return;
83
+ }
84
+ if (featureId === undefined) {
85
+ // The argument is optional only for the mode that names no feature.
86
+ // Refusing here rather than making the whole command feature-less
87
+ // keeps one rule: every pin says which documents it read.
88
+ return fail(json, "invalid-option", "loam rebase needs a feature id — e.g. `loam rebase FEAT-101`. To pin the living corpus's `Realizes:` entries instead, run `loam rebase --living`.");
89
+ }
90
+ await rebaseLocked(config.docsDir, featureId, { ...opts, recovered });
91
+ }
92
+ finally {
93
+ await release();
94
+ }
95
+ });
96
+ }
97
+ async function rebaseLocked(docsDir, featureId, opts) {
98
+ const json = opts.json === true;
99
+ const dryRun = opts.dryRun === true;
100
+ const feature = await resolveFeature(docsDir, featureId, "exclude");
101
+ if (!feature) {
102
+ return fail(json, "unknown-target", await missingFeatureMessage(docsDir, featureId));
103
+ }
104
+ const { id } = feature;
105
+ // The enumeration's own id is what travels on when --service names one
106
+ // (`entry.id` rather than the argument — repo/service-target.ts's rule):
107
+ // equal as strings, and only one of them was produced by a readdir.
108
+ const chosen = opts.service === undefined ? undefined : feature.services.find((s) => s === opts.service);
109
+ if (opts.service !== undefined && chosen === undefined) {
110
+ // The refusal names the choices, the way `loam delta`'s does: a typo must
111
+ // never be indistinguishable from a feature with nothing to pin.
112
+ return fail(json, "unknown-target", `${id} carries no requirement delta for '${opts.service}'` +
113
+ (feature.services.length === 0
114
+ ? " — it touches no service at all."
115
+ : ` — it touches: ${[...feature.services].sort(compareIds).join(", ")}.`));
116
+ }
117
+ const services = (chosen === undefined ? [...feature.services] : [chosen]).sort(compareIds);
118
+ // One read index for the whole loop below: every planner resolves its
119
+ // living document through `locateServicePaths`, and without a shared
120
+ // context each of those calls is a fresh fleet walk — services × axes of
121
+ // them per run, on a tree that cannot change under the lock this holds.
122
+ const repo = { docsDir, fleet: new FleetContext() };
123
+ const outcomes = [];
124
+ const capabilityOutcomes = [];
125
+ const writes = [];
126
+ // The business corpus this feature changes. Skipped entirely under `--service`:
127
+ // that flag means "restrict to one service", and a capability names none — so
128
+ // pinning capability deltas under it would write files the caller narrowed the
129
+ // command to exclude. One `existsSync` when the feature carries no capability
130
+ // delta, which is every feature in a fleet that has not adopted the axis.
131
+ const capabilities = chosen === undefined ? (await repo.fleet.featureCapabilityDeltas(feature.dir)).docs : [];
132
+ try {
133
+ for (const service of services) {
134
+ for (const axis of SPEC_AXES) {
135
+ const specPath = featureSpecPaths(feature.dir, service)[axis.key];
136
+ if (!existsSync(specPath))
137
+ continue;
138
+ const planned = await planAxis(repo, service, axis, specPath);
139
+ outcomes.push(...planned.outcomes);
140
+ if (planned.content !== null)
141
+ writes.push({ path: specPath, content: planned.content });
142
+ }
143
+ // The contract axis. It needs the pin more than the requirement axes do:
144
+ // a requirement delta spells only the requirements it changes, while an
145
+ // openapi delta is a COMPLETE document and spells the whole contract.
146
+ const openapiPath = featureSpecPaths(feature.dir, service).openapi;
147
+ if (existsSync(openapiPath)) {
148
+ const planned = await planOpenapi(repo, service, openapiPath);
149
+ outcomes.push(...planned.outcomes);
150
+ if (planned.content !== null)
151
+ writes.push({ path: openapiPath, content: planned.content });
152
+ }
153
+ // The event axis, for the openapi reason: a feature's asyncapi.yaml is
154
+ // a complete document too, so its quotes need pins or the merge cannot
155
+ // tell them from edits.
156
+ const asyncapiPath = featureSpecPaths(feature.dir, service).asyncapi;
157
+ if (existsSync(asyncapiPath)) {
158
+ const planned = await planAsyncapi(repo, service, asyncapiPath);
159
+ outcomes.push(...planned.outcomes);
160
+ if (planned.content !== null)
161
+ writes.push({ path: asyncapiPath, content: planned.content });
162
+ }
163
+ }
164
+ // The business axis, after the services and in the same transaction: a
165
+ // feature that changes a promise and the services keeping it is ONE act,
166
+ // and half of it pinned is the state the journal exists to prevent.
167
+ for (const doc of capabilities) {
168
+ const planned = await planCapabilityAxis(repo, doc.id, doc.spec);
169
+ capabilityOutcomes.push(...planned.outcomes);
170
+ if (planned.content !== null)
171
+ writes.push({ path: doc.spec, content: planned.content });
172
+ }
173
+ }
174
+ catch (err) {
175
+ if (!(err instanceof NotUtf8DocumentError))
176
+ throw err;
177
+ // A pin is a claim about text somebody read. Over a document loam could not
178
+ // decode it would be a digest of mojibake with loam's name on it — the
179
+ // precise false claim this command exists to prevent — and the next archive
180
+ // would land a stale delta over living text on the strength of it. So it
181
+ // refuses by name and writes nothing, exactly as `loam archive` refuses a
182
+ // merge it could not read, under archive's code. The refusal is here, in
183
+ // the PLAN, so it lands before stageWrites: no file is touched, and a
184
+ // feature spanning four services does not get two of them pinned.
185
+ return fail(json, "merge-failed", `rebase ${id} failed — nothing was pinned: ${err.message}`);
186
+ }
187
+ if (!dryRun && writes.length > 0) {
188
+ // Staged and committed through the journaled transaction: a feature
189
+ // spanning four services is one act, and neither a full disk between its
190
+ // second and third file NOR a kill between two renames may leave two of
191
+ // them pinned to a reading the other two never saw — the journal makes
192
+ // the second case recoverable where rollback alone could not see it.
193
+ const staged = await stageWrites(writes);
194
+ const committed = await commitStaged({ root: docsDir, command: "rebase", rerun: `loam rebase ${id}`, target: id }, staged, "pinned");
195
+ if (!committed.ok) {
196
+ return fail(json, committed.code, committed.code === "merge-failed" ? `rebase ${id} failed — nothing was pinned: ${committed.message}` : committed.message);
197
+ }
198
+ }
199
+ // One line per pin, whichever corpus it came from: what to call the document,
200
+ // and the record itself. The two lists are reported separately in `--json`
201
+ // (the published `pins[]`/`services[]` keys keep meaning exactly what they
202
+ // meant) and together here, because a human rebasing one feature wants one
203
+ // list in one order.
204
+ const lines = [
205
+ ...outcomes.map((o) => ({ where: `${o.service}/${o.file}`, pin: o })),
206
+ ...capabilityOutcomes.map((o) => ({ where: `capabilities/${o.capability}/${o.file}`, pin: o })),
207
+ ];
208
+ const changed = lines.filter((l) => l.pin.status === "pinned" || l.pin.status === "repinned");
209
+ const unresolved = lines.filter((l) => l.pin.status === "unresolved");
210
+ if (json) {
211
+ emitJson({
212
+ command: "rebase",
213
+ feature: id,
214
+ services,
215
+ dryRun,
216
+ pins: outcomes,
217
+ // Additive, and separate from `pins[]` on purpose: `PinOutcome.service`
218
+ // is a published field, and a capability id written into it would say
219
+ // something false to every consumer that already reads it.
220
+ capabilities: capabilities.map((c) => c.id),
221
+ capabilityPins: capabilityOutcomes,
222
+ written: dryRun ? [] : writes.map((w) => repoPath(docsDir, w.path)),
223
+ ...(opts.recovered === null ? {} : { recovered: opts.recovered }),
224
+ });
225
+ return;
226
+ }
227
+ if (lines.length === 0) {
228
+ console.log(`${id}: nothing to pin — a baseline only means something for a requirement, an operation, a path-level key, a component or an event slot that already exists in the living docs.`);
229
+ return;
230
+ }
231
+ if (opts.recovered !== null)
232
+ console.log(`${sayRecovered(opts.recovered)}\n`);
233
+ console.log(`${id}${dryRun ? " (dry run)" : ""}\n`);
234
+ for (const { where, pin: o } of lines) {
235
+ const what = `${where} ${o.kind} ${o.target}`;
236
+ if (o.status === "unresolved") {
237
+ console.log(` · ${what} — not in the living docs yet, nothing to pin`);
238
+ }
239
+ else if (o.status === "unwritable") {
240
+ console.log(` ! ${what} — a YAML alias (or a key loam cannot address by name); loam will not stamp through it`);
241
+ }
242
+ else if (o.status === "unchanged") {
243
+ console.log(` = ${what} — already ${o.to}`);
244
+ }
245
+ else if (o.status === "pinned") {
246
+ console.log(` + ${what} — pinned ${o.to}`);
247
+ }
248
+ else {
249
+ console.log(` ~ ${what} — ${o.from} → ${o.to}`);
250
+ }
251
+ }
252
+ if (changed.length === 0) {
253
+ console.log(`\nNothing to write: every pin already names the living version.`);
254
+ }
255
+ else if (dryRun) {
256
+ console.log(`\n${plural(changed.length, "pin")} would be written. Nothing was written.`);
257
+ }
258
+ else {
259
+ console.log(`\n${plural(changed.length, "pin")} written across ${plural(writes.length, "file")}.`);
260
+ // The one sentence that keeps this command honest. A pin is a claim about
261
+ // what its author read, and a `repinned` line means the living text moved
262
+ // under a delta that still says what it said before — restamping does not
263
+ // fold in the change it just stopped reporting.
264
+ const repinned = changed.filter((l) => l.pin.status === "repinned");
265
+ if (repinned.length > 0) {
266
+ console.log(`\n⚠ ${plural(repinned.length, "pin")} moved since this delta was written. A pin records what you read — ` +
267
+ `re-read those living requirements and operations and fold in what you still mean, or the next archive lands your version over theirs with loam's blessing.`);
268
+ }
269
+ }
270
+ if (unresolved.length > 0) {
271
+ console.log(`\n${plural(unresolved.length, "item")} could not be pinned because the living docs do not have them yet — ` +
272
+ `for a requirement, \`loam dependencies ${id}\` says which feature introduces it first; for an operation, this feature is adding it.`);
273
+ }
274
+ }
275
+ //# sourceMappingURL=rebase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebase.js","sourceRoot":"","sources":["../../../src/commands/rebase/rebase.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { NotUtf8DocumentError } from \"../../core/kernel/document-bytes.js\";\nimport { InvalidIdError, assertServiceId } from \"../../core/kernel/ids/service.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { compareIds } from \"../../core/repo/entries.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../../core/repo/paths.js\";\nimport { missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport { stageWrites } from \"../../core/staging/commit.js\";\nimport { type CommitRecovery, InterruptedCommitError } from \"../../core/staging/interrupted.js\";\nimport { acquireDocsLock, DocsBusyError } from \"../../core/staging/lock.js\";\nimport { recoverInterruptedCommit } from \"../../core/staging/recovery/recover.js\";\nimport { commitStaged } from \"../../core/staging/txn/transaction.js\";\nimport { type PlannedWrite } from \"../../core/staging/writes.js\";\nimport { plural, sayRecovered } from \"../policy/format.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport {\n planAsyncapi,\n planAxis,\n planCapabilityAxis,\n planOpenapi,\n type CapabilityPinOutcome,\n type PinOutcome,\n} from \"./plan.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { rebaseLivingLocked } from \"./living.js\";\n\n/**\n * `loam rebase` — pin a feature's MODIFIED/REMOVED requirements to the living\n * text they are written against.\n *\n * The counterpart to `loam vouch`: vouch stamps a digest of the CODE a living\n * spec was written from, this stamps a digest of the LIVING REQUIREMENT a delta\n * was written from. Both exist so a later `loam validate` can tell \"still true\"\n * from \"something moved underneath\", and both are worth exactly what they claim\n * — so this command computes every pin from what is on disk right now and never\n * invents one for a requirement that addresses nothing living.\n *\n * It is the fix `delta.baseline-stale` sends people to, which is why the\n * rewrite is line SURGERY rather than a reserialize: a delta document is\n * authored prose — section headings, comments, the order the author chose — and\n * `serializeRequirements` would flatten all of it into a bare requirement run.\n * One line changes per requirement; every other byte is copied.\n *\n * Restamping is the LAST step of resolving a collision, never the resolution\n * itself: a pin says \"I read this version\", so running it without re-reading\n * makes it a lie with a digest on it. The output says so on the way out.\n */\n\ninterface RebaseOptions {\n service?: string;\n json?: boolean;\n dryRun?: boolean;\n living?: boolean;\n}\n\n/** What happened to one pin. */\n\nexport function registerRebase(program: Command): void {\n program\n .command(\"rebase\")\n // Optional ONLY because `--living` names no feature: that mode pins the\n // living corpus itself. Every existing invocation still parses, which is\n // what makes the relaxation additive rather than a contract change.\n .argument(\"[featureId]\", \"feature id, e.g. FEAT-101 — omitted with --living\")\n .description(\"Pin a feature's MODIFIED/REMOVED requirements to the living text they are written against\")\n .option(\"--service <id>\", \"restrict to one service (default: every service the feature touches)\")\n .option(\n \"--living\",\n \"pin the LIVING corpus's `Realizes:` entries to the capability requirements they name, instead of a feature's deltas\",\n )\n .option(\n \"--dry-run\",\n \"print what would be pinned and write nothing — beyond first finishing a predecessor's interrupted commit, exactly as a real run would\",\n )\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureId: string | undefined, opts: RebaseOptions) => {\n const json = opts.json === true;\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n\n // The id grammar on the RAW argument, before it reaches a path join —\n // one grammar for the whole tool (core/kernel/ids/service.ts).\n if (opts.service !== undefined) {\n try {\n assertServiceId(opts.service, \"--service\");\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n }\n\n // The living specs must not move between the read that computes a pin and\n // the write that records it: a digest taken over a document an archive\n // replaced a millisecond later is precisely the false \"I read this\" the\n // pin exists to make impossible. One writer per docs repo, refusing\n // rather than queueing, exactly as archive takes it.\n let release: () => Promise<void>;\n try {\n release = await acquireDocsLock(config.docsDir);\n } catch (err) {\n if (!(err instanceof DocsBusyError)) throw err;\n return fail(json, \"docs-busy\", err.message);\n }\n try {\n // A journal in the repo means the LAST write never finished — reading\n // living documents to compute pins over a half-commit would digest\n // bytes no run ever produced. Recover (or refuse) first, under the\n // same lock the commit will hold.\n let recovered: CommitRecovery | null;\n try {\n recovered = await recoverInterruptedCommit(config.docsDir);\n } catch (err) {\n if (!(err instanceof InterruptedCommitError)) throw err;\n return fail(json, \"commit-interrupted\", err.message);\n }\n if (opts.living === true) {\n await rebaseLivingLocked(config.docsDir, opts);\n return;\n }\n if (featureId === undefined) {\n // The argument is optional only for the mode that names no feature.\n // Refusing here rather than making the whole command feature-less\n // keeps one rule: every pin says which documents it read.\n return fail(\n json,\n \"invalid-option\",\n \"loam rebase needs a feature id — e.g. `loam rebase FEAT-101`. To pin the living corpus's `Realizes:` entries instead, run `loam rebase --living`.\",\n );\n }\n await rebaseLocked(config.docsDir, featureId, { ...opts, recovered });\n } finally {\n await release();\n }\n });\n}\n\nasync function rebaseLocked(\n docsDir: DocsDir,\n featureId: string,\n opts: RebaseOptions & { recovered: CommitRecovery | null },\n): Promise<void> {\n const json = opts.json === true;\n const dryRun = opts.dryRun === true;\n\n const feature = await resolveFeature(docsDir, featureId, \"exclude\");\n if (!feature) {\n return fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, featureId));\n }\n const { id } = feature;\n\n // The enumeration's own id is what travels on when --service names one\n // (`entry.id` rather than the argument — repo/service-target.ts's rule):\n // equal as strings, and only one of them was produced by a readdir.\n const chosen = opts.service === undefined ? undefined : feature.services.find((s) => s === opts.service);\n if (opts.service !== undefined && chosen === undefined) {\n // The refusal names the choices, the way `loam delta`'s does: a typo must\n // never be indistinguishable from a feature with nothing to pin.\n return fail(\n json,\n \"unknown-target\",\n `${id} carries no requirement delta for '${opts.service}'` +\n (feature.services.length === 0\n ? \" — it touches no service at all.\"\n : ` — it touches: ${[...feature.services].sort(compareIds).join(\", \")}.`),\n );\n }\n const services = (chosen === undefined ? [...feature.services] : [chosen]).sort(compareIds);\n\n // One read index for the whole loop below: every planner resolves its\n // living document through `locateServicePaths`, and without a shared\n // context each of those calls is a fresh fleet walk — services × axes of\n // them per run, on a tree that cannot change under the lock this holds.\n const repo = { docsDir, fleet: new FleetContext() };\n const outcomes: PinOutcome[] = [];\n const capabilityOutcomes: CapabilityPinOutcome[] = [];\n const writes: PlannedWrite[] = [];\n // The business corpus this feature changes. Skipped entirely under `--service`:\n // that flag means \"restrict to one service\", and a capability names none — so\n // pinning capability deltas under it would write files the caller narrowed the\n // command to exclude. One `existsSync` when the feature carries no capability\n // delta, which is every feature in a fleet that has not adopted the axis.\n const capabilities =\n chosen === undefined ? (await repo.fleet.featureCapabilityDeltas(feature.dir)).docs : [];\n try {\n for (const service of services) {\n for (const axis of SPEC_AXES) {\n const specPath = featureSpecPaths(feature.dir, service)[axis.key];\n if (!existsSync(specPath)) continue;\n const planned = await planAxis(repo, service, axis, specPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: specPath, content: planned.content });\n }\n // The contract axis. It needs the pin more than the requirement axes do:\n // a requirement delta spells only the requirements it changes, while an\n // openapi delta is a COMPLETE document and spells the whole contract.\n const openapiPath = featureSpecPaths(feature.dir, service).openapi;\n if (existsSync(openapiPath)) {\n const planned = await planOpenapi(repo, service, openapiPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: openapiPath, content: planned.content });\n }\n // The event axis, for the openapi reason: a feature's asyncapi.yaml is\n // a complete document too, so its quotes need pins or the merge cannot\n // tell them from edits.\n const asyncapiPath = featureSpecPaths(feature.dir, service).asyncapi;\n if (existsSync(asyncapiPath)) {\n const planned = await planAsyncapi(repo, service, asyncapiPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: asyncapiPath, content: planned.content });\n }\n }\n // The business axis, after the services and in the same transaction: a\n // feature that changes a promise and the services keeping it is ONE act,\n // and half of it pinned is the state the journal exists to prevent.\n for (const doc of capabilities) {\n const planned = await planCapabilityAxis(repo, doc.id, doc.spec);\n capabilityOutcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: doc.spec, content: planned.content });\n }\n } catch (err) {\n if (!(err instanceof NotUtf8DocumentError)) throw err;\n // A pin is a claim about text somebody read. Over a document loam could not\n // decode it would be a digest of mojibake with loam's name on it — the\n // precise false claim this command exists to prevent — and the next archive\n // would land a stale delta over living text on the strength of it. So it\n // refuses by name and writes nothing, exactly as `loam archive` refuses a\n // merge it could not read, under archive's code. The refusal is here, in\n // the PLAN, so it lands before stageWrites: no file is touched, and a\n // feature spanning four services does not get two of them pinned.\n return fail(json, \"merge-failed\", `rebase ${id} failed — nothing was pinned: ${err.message}`);\n }\n\n if (!dryRun && writes.length > 0) {\n // Staged and committed through the journaled transaction: a feature\n // spanning four services is one act, and neither a full disk between its\n // second and third file NOR a kill between two renames may leave two of\n // them pinned to a reading the other two never saw — the journal makes\n // the second case recoverable where rollback alone could not see it.\n const staged = await stageWrites(writes);\n const committed = await commitStaged(\n { root: docsDir, command: \"rebase\", rerun: `loam rebase ${id}`, target: id },\n staged,\n \"pinned\",\n );\n if (!committed.ok) {\n return fail(\n json,\n committed.code,\n committed.code === \"merge-failed\" ? `rebase ${id} failed — nothing was pinned: ${committed.message}` : committed.message,\n );\n }\n }\n\n // One line per pin, whichever corpus it came from: what to call the document,\n // and the record itself. The two lists are reported separately in `--json`\n // (the published `pins[]`/`services[]` keys keep meaning exactly what they\n // meant) and together here, because a human rebasing one feature wants one\n // list in one order.\n const lines = [\n ...outcomes.map((o) => ({ where: `${o.service}/${o.file}`, pin: o })),\n ...capabilityOutcomes.map((o) => ({ where: `capabilities/${o.capability}/${o.file}`, pin: o })),\n ];\n const changed = lines.filter((l) => l.pin.status === \"pinned\" || l.pin.status === \"repinned\");\n const unresolved = lines.filter((l) => l.pin.status === \"unresolved\");\n\n if (json) {\n emitJson({\n command: \"rebase\",\n feature: id,\n services,\n dryRun,\n pins: outcomes,\n // Additive, and separate from `pins[]` on purpose: `PinOutcome.service`\n // is a published field, and a capability id written into it would say\n // something false to every consumer that already reads it.\n capabilities: capabilities.map((c) => c.id),\n capabilityPins: capabilityOutcomes,\n written: dryRun ? [] : writes.map((w) => repoPath(docsDir, w.path)),\n ...(opts.recovered === null ? {} : { recovered: opts.recovered }),\n });\n return;\n }\n\n if (lines.length === 0) {\n console.log(\n `${id}: nothing to pin — a baseline only means something for a requirement, an operation, a path-level key, a component or an event slot that already exists in the living docs.`,\n );\n return;\n }\n\n if (opts.recovered !== null) console.log(`${sayRecovered(opts.recovered)}\\n`);\n console.log(`${id}${dryRun ? \" (dry run)\" : \"\"}\\n`);\n for (const { where, pin: o } of lines) {\n const what = `${where} ${o.kind} ${o.target}`;\n if (o.status === \"unresolved\") {\n console.log(` · ${what} — not in the living docs yet, nothing to pin`);\n } else if (o.status === \"unwritable\") {\n console.log(` ! ${what} — a YAML alias (or a key loam cannot address by name); loam will not stamp through it`);\n } else if (o.status === \"unchanged\") {\n console.log(` = ${what} — already ${o.to}`);\n } else if (o.status === \"pinned\") {\n console.log(` + ${what} — pinned ${o.to}`);\n } else {\n console.log(` ~ ${what} — ${o.from} → ${o.to}`);\n }\n }\n\n if (changed.length === 0) {\n console.log(`\\nNothing to write: every pin already names the living version.`);\n } else if (dryRun) {\n console.log(`\\n${plural(changed.length, \"pin\")} would be written. Nothing was written.`);\n } else {\n console.log(`\\n${plural(changed.length, \"pin\")} written across ${plural(writes.length, \"file\")}.`);\n // The one sentence that keeps this command honest. A pin is a claim about\n // what its author read, and a `repinned` line means the living text moved\n // under a delta that still says what it said before — restamping does not\n // fold in the change it just stopped reporting.\n const repinned = changed.filter((l) => l.pin.status === \"repinned\");\n if (repinned.length > 0) {\n console.log(\n `\\n⚠ ${plural(repinned.length, \"pin\")} moved since this delta was written. A pin records what you read — ` +\n `re-read those living requirements and operations and fold in what you still mean, or the next archive lands your version over theirs with loam's blessing.`,\n );\n }\n }\n if (unresolved.length > 0) {\n console.log(\n `\\n${plural(unresolved.length, \"item\")} could not be pinned because the living docs do not have them yet — ` +\n `for a requirement, \\`loam dependencies ${id}\\` says which feature introduces it first; for an operation, this feature is adding it.`,\n );\n }\n}\n"]}