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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Where every file of the feature will be once this archive lands — the half of
3
+ * the link rewrite that `core/links/parse.ts`'s `rebaseLinks` cannot know.
4
+ *
5
+ * THE DEFECT THIS CLOSES. The requirement merge has re-expressed a delta's
6
+ * relative links from the directory they were written in to the one they land
7
+ * in since the glossary axis shipped. But it re-expressed them against the
8
+ * target's OLD place, and the same archive moves the targets that sit inside
9
+ * the feature: a link from a delta to `features/<FEAT>/glossary/order.md` was
10
+ * rewritten faithfully into the living spec and resolved to nothing one rename
11
+ * later, when the term had been copied to `glossary/order.md` and the feature
12
+ * directory had gone to `features/archive/<FEAT>/`. Every such link was
13
+ * `link.unresolved` on the first `validate --all` after a clean archive — an
14
+ * error the feature gate could not see, because before the archive the link
15
+ * was correct.
16
+ *
17
+ * TWO RULES, and they are the archive's own moves rather than a guess:
18
+ *
19
+ * - A file the archive COPIES into the living tree — a glossary term, a flow,
20
+ * a topology document — is cited at its living copy. That is the document
21
+ * readers are meant to find, and the one `glossary.unlinked` counts a
22
+ * citation of: a link left on the archived copy would resolve and still
23
+ * leave the living term reading as a word nobody uses.
24
+ * - Every other file under the feature directory — its ADRs above all, the
25
+ * intent, the deltas themselves — is cited where the feature now lives,
26
+ * `features/archive/<FEAT>/`. Nothing else copies a feature ADR anywhere: it
27
+ * records a decision taken for this change, and it stays with the change
28
+ * (SCHEMA "Layout" says so beside the slot).
29
+ *
30
+ * Built per call from three directory walks — the glossary, the flows and the
31
+ * topology documents, the same walks `../specs.ts` and `../landscape.ts` make
32
+ * to copy them; a `Gated` field would be one more value the gate computes for
33
+ * a phase it does not own.
34
+ *
35
+ * THE OTHER TWO HALVES ARE THE LINKS NO MERGE CARRIES. The feature's own
36
+ * documents (`planFeatureLinks` below) stay in the feature and ride the rename,
37
+ * so their links out of it are one directory short the moment it lands. Every
38
+ * other document in the corpus (`planInboundLinks` below) stays where it is
39
+ * while the files it cites inside the feature move away from under it.
40
+ */
41
+ import { dirname, resolve } from "node:path";
42
+ import { repoPath } from "../../../../core/envelope/json.js";
43
+ import { movedUnderVouch, parseFrontmatter, stringField } from "../../../../core/document/frontmatter.js";
44
+ import { parseRequirements } from "../../../../core/document/parse.js";
45
+ import { requirementDigest } from "../../../../core/document/spec.js";
46
+ import { featureGlossary, livingTermPath } from "../../../../core/glossary/delta.js";
47
+ import { allAuthoredDocuments, movedMarkdown } from "../../../../core/links/corpus.js";
48
+ import { rebaseLinks } from "../../../../core/links/parse.js";
49
+ import { inOrder } from "../../../../core/kernel/concurrency.js";
50
+ import { acrossRename, isPathInside } from "../../../../core/kernel/path-safety.js";
51
+ import { featuresDir, servicePathsAt } from "../../../../core/repo/paths.js";
52
+ import { listServices } from "../../../../core/repo/repo.js";
53
+ import { featureFlows, livingFlowPath } from "../../../../core/usecases/delta/flows.js";
54
+ import { featureDeployments, livingDeploymentPath } from "../../../../core/deployment/delta.js";
55
+ import { NotUtf8Error, planWrite, readUtf8 } from "../../../../core/staging/writes.js";
56
+ export async function featureRetarget(docsDir, gated) {
57
+ const copies = new Map();
58
+ for (const term of (await featureGlossary(gated.featureDir)).terms) {
59
+ copies.set(resolve(term.path), resolve(livingTermPath(docsDir, term.id)));
60
+ }
61
+ for (const flow of await featureFlows(gated.featureDir)) {
62
+ copies.set(resolve(flow.path), resolve(livingFlowPath(docsDir, flow.rel)));
63
+ }
64
+ for (const doc of await featureDeployments(gated.featureDir)) {
65
+ copies.set(resolve(doc.path), resolve(livingDeploymentPath(docsDir, doc.rel)));
66
+ }
67
+ return (target) => copies.get(resolve(target)) ?? followsFeature(gated, target);
68
+ }
69
+ /**
70
+ * The feature's own markdown documents with their relative links re-expressed
71
+ * for `features/archive/<FEAT>/`, planned at their CURRENT paths so the swap
72
+ * lands before the rename carries them (`../../run.ts`).
73
+ *
74
+ * THE DEFECT THIS CLOSES. The archive is a directory rename, and every document
75
+ * that stays in the feature — the intent, its ADRs, the deltas, a capability
76
+ * delta, the archived copy of a term — kept links written one directory
77
+ * shallower than where they now sit: `../../services/x/spec.md` from
78
+ * `features/<FEAT>/intent.md` names `features/services/x/spec.md` once the
79
+ * intent is `features/archive/<FEAT>/intent.md`. Nothing grades an archived
80
+ * feature (`core/links/corpus.ts` says why), so nothing said so — but the
81
+ * living documents link INTO the archived feature's ADRs (`featureRetarget`
82
+ * above), and a reader who followed one landed on a decision whose own links
83
+ * were all off by one.
84
+ *
85
+ * ONE RULE, the rename's: a target inside the feature moves with it and every
86
+ * other target stays put — `subsystem move`'s rule for its own renames
87
+ * (`commands/subsystem/txn/links.ts`). So a link from the intent to its own
88
+ * ADR or its own glossary term is left exactly as written: the archived
89
+ * record keeps citing the archived copy, which is the text the feature was
90
+ * reviewed against, rather than being redirected to a living copy that will go
91
+ * on changing without it. Only the routes the rename broke are touched, and a
92
+ * link whose text still reaches its target keeps its spelling
93
+ * (`rebaseLinks`).
94
+ *
95
+ * The undo is the snapshot's, like every other write here: each rewritten
96
+ * document is a staged write, so its pre-image is parked and `loam unarchive`
97
+ * puts the authored bytes back (`core/staging/snapshot/snapshot.ts` says where
98
+ * it records them).
99
+ *
100
+ * A document that is not UTF-8 is left exactly as written, and the text
101
+ * output says so — `--json` has no key for it, and minting one for a document
102
+ * nothing grades was not worth a contract change. Every other read here would
103
+ * refuse it, but this one only corrects links, and refusing the archive over
104
+ * such a document would be the larger harm.
105
+ *
106
+ * Read with bounded concurrency and applied in input order (`inOrder`), as
107
+ * `subsystem move`'s rewrite reads its own: a large feature — one
108
+ * `loam migrate-openspec` filled with its legacy documents — is read in full
109
+ * under the lock.
110
+ */
111
+ export async function planFeatureLinks(config, gated, planned, say) {
112
+ const { docsDir } = config;
113
+ const retarget = (target) => followsFeature(gated, target);
114
+ const read = await inOrder(await movedMarkdown(gated.featureDir), async (path) => {
115
+ try {
116
+ return { path, text: await readUtf8(path) };
117
+ }
118
+ catch (err) {
119
+ if (!(err instanceof NotUtf8Error))
120
+ throw err;
121
+ return { path, text: null };
122
+ }
123
+ });
124
+ const relinked = [];
125
+ for (const { path, text } of read) {
126
+ const lands = followsFeature(gated, path);
127
+ if (text === null) {
128
+ say(` ⚠ ${repoPath(docsDir, lands)} is not UTF-8 — its links were left as written`);
129
+ continue;
130
+ }
131
+ const next = rebaseLinks(text, { from: dirname(path), to: dirname(lands), retarget });
132
+ if (next === text)
133
+ continue;
134
+ planned.writes.push(planWrite(path, next));
135
+ relinked.push(repoPath(docsDir, lands));
136
+ }
137
+ if (relinked.length === 0)
138
+ return;
139
+ say(` feature documents: ${relinked.length} relinked for ${repoPath(docsDir, gated.archiveDest)}/`);
140
+ for (const path of relinked)
141
+ say(` ${path}`);
142
+ }
143
+ /**
144
+ * Every other document in the link corpus whose links reach INTO the feature,
145
+ * re-expressed for where this archive leaves what they cite.
146
+ *
147
+ * THE DEFECT THIS CLOSES. Another change in flight builds on this one's
148
+ * decision (`../FEAT/adrs/0001-x.md` from its intent), or a living ADR cites
149
+ * the decision taken for it while the change is still open. Correct the moment
150
+ * before the archive, so no gate could say otherwise; `link.unresolved` the
151
+ * moment after, because the rename took the directory away from under the
152
+ * link. And no edit the author could make in advance would have helped: the
153
+ * archived path does not exist until the archive does. `subsystem move`
154
+ * rewrites the links into the directories it renames
155
+ * (`commands/subsystem/txn/links.ts`); this is the archive's rename owing the
156
+ * same correction.
157
+ *
158
+ * THE RULE IS `featureRetarget`'s, not the rename's. These documents are not
159
+ * the record of the change; they are the living corpus and the other changes
160
+ * in flight, read against the tree as it is. So a term the archive copies into
161
+ * `glossary/` is cited at the living copy — the one a reader is meant to find
162
+ * and the one `glossary.unlinked` counts — and everything that stays with the
163
+ * change, its ADRs and intent above all, is cited where the feature now lives.
164
+ * Only the routes into the feature change; every other link keeps its
165
+ * spelling (`rebaseLinks`).
166
+ *
167
+ * COMPOSED ONTO THE MERGE. A living spec the merge rewrites can cite the
168
+ * feature in its own narrative, and relinking the bytes on disk would plan a
169
+ * second write of its PRE-merge text — swapped after the merge's, it would
170
+ * undo the merge. So a document this plan already writes is relinked from the
171
+ * text the merge composed, and that one write is replaced in place.
172
+ *
173
+ * THE COSTS ARE SAID THE WAY THE MERGE SAYS ITS OWN. A rewritten route is still
174
+ * a changed byte. Under a vouch stamp it is `content.stale` until the owner
175
+ * re-vouches, so a vouched spec this moves joins `revouchOwed` exactly as one
176
+ * the merge moves does. Inside a living requirement it moves the digest a
177
+ * `Based-On:` pin holds, and the plan names the document. No git refusal of the
178
+ * kind `subsystem move` holds (`move-uncommitted`): archive never stages in
179
+ * git, and its undo is the snapshot — every write here is one of its rows, so
180
+ * `loam unarchive` puts the authored bytes back, a document edited since is
181
+ * `snapshot-stale` like any other, and one whose feature has been archived or
182
+ * removed since is left as it is (`commands/unarchive/restore.ts`).
183
+ *
184
+ * The two notes are the text output's alone — the walk that could not be made
185
+ * and the `Based-On:` cost. `--json` has no key for either: its `warnings` are
186
+ * coherence issues with codes, and a new key for an advisory the next
187
+ * `validate --all` grades in full was not worth a contract change. What an
188
+ * agent acts on is in the payload already: every relinked document is a `plan`
189
+ * row and every re-vouch it owes is on `revouchOwed`.
190
+ */
191
+ export async function planInboundLinks(config, gated, planned, say) {
192
+ const { docsDir, fleet } = config;
193
+ let corpus;
194
+ try {
195
+ corpus = await allAuthoredDocuments(docsDir, fleet);
196
+ }
197
+ catch (err) {
198
+ // The gate enumerated this fleet already, so a walk that fails here is a
199
+ // tree `validate` will diagnose. The archive is not refused over a question
200
+ // it asks to keep the corpus readable — `subsystem move` decides the same —
201
+ // but it does not pretend the question was answered either.
202
+ say(` ⚠ links INTO ${repoPath(docsDir, gated.featureDir)}/ were not checked — the fleet's documents could not be walked ` +
203
+ `(${err instanceof Error ? err.message : String(err)}); \`loam validate --all\` names any that broke.`);
204
+ return;
205
+ }
206
+ const retarget = await featureRetarget(docsDir, gated);
207
+ const vouchable = new Map();
208
+ for (const service of await listServices(docsDir, fleet)) {
209
+ const paths = servicePathsAt(service.dir);
210
+ vouchable.set(resolve(paths.spec), service.id);
211
+ vouchable.set(resolve(paths.archSpec), service.id);
212
+ }
213
+ const plannedAt = new Map(planned.writes.map((write, at) => [resolve(write.path), { at, write }]));
214
+ const seen = new Set();
215
+ const candidates = corpus.filter((path) => {
216
+ const key = resolve(path);
217
+ if (seen.has(key) || isPathInside(gated.featureDir, path))
218
+ return false;
219
+ seen.add(key);
220
+ return true;
221
+ });
222
+ const documents = await inOrder(candidates, async (path) => {
223
+ const composed = plannedAt.get(resolve(path));
224
+ if (composed !== undefined) {
225
+ // A delete or a Buffer is not text this rewrite may compose onto; no
226
+ // markdown planner hands over either.
227
+ const { content } = composed.write;
228
+ return { path, composed, text: typeof content === "string" ? content : null };
229
+ }
230
+ try {
231
+ return { path, composed, text: await readUtf8(path) };
232
+ }
233
+ catch {
234
+ // Absent (the corpus names every slot a service MAY fill) or not UTF-8,
235
+ // which `link.unreadable` already names: nothing loam could write back.
236
+ return { path, composed, text: null };
237
+ }
238
+ });
239
+ const pinned = (md) => parseRequirements(md).map(requirementDigest).join(",");
240
+ const relinked = [];
241
+ const notes = [];
242
+ for (const { path, composed, text } of documents) {
243
+ if (text === null)
244
+ continue;
245
+ const next = rebaseLinks(text, { from: dirname(path), to: dirname(path), retarget });
246
+ if (next === text)
247
+ continue;
248
+ // In place, at the index the merge's write already holds: nothing between
249
+ // here and the map above adds, removes or reorders a planned write.
250
+ if (composed === undefined)
251
+ planned.writes.push(planWrite(path, next));
252
+ else
253
+ planned.writes[composed.at] = { ...composed.write, content: next };
254
+ const where = repoPath(docsDir, path);
255
+ relinked.push(where);
256
+ // The merge's own question (`../specs.ts`), asked of the bytes about to be
257
+ // written — and once per document: a spec the merge already named is not
258
+ // owed a second re-vouch for the same commit.
259
+ const service = vouchable.get(resolve(path));
260
+ const fm = parseFrontmatter(next);
261
+ const named = planned.revouchOwed.some((owed) => resolve(owed.path) === resolve(path));
262
+ if (service !== undefined && !named && movedUnderVouch(fm, next)) {
263
+ planned.revouchOwed.push({ service, path, owner: stringField(fm, "owner") ?? null });
264
+ }
265
+ // Living documents only: a pin holds the digest of a LIVING requirement,
266
+ // so another feature's own delta text moving pins nothing.
267
+ if (!isPathInside(featuresDir(docsDir), path) && pinned(next) !== pinned(text)) {
268
+ notes.push(`${where}: links this archive rewrote sit inside requirements, so a feature in flight that pins one with Based-On: ` +
269
+ "reports delta.baseline-stale until `loam rebase` re-pins it.");
270
+ }
271
+ }
272
+ if (relinked.length === 0)
273
+ return;
274
+ say(` links into the feature: ${relinked.length} document(s) relinked for ${repoPath(docsDir, gated.archiveDest)}/`);
275
+ for (const path of relinked)
276
+ say(` ${path}`);
277
+ for (const note of notes)
278
+ say(` note: ${note}`);
279
+ }
280
+ /** Where `target` will be once the feature directory has moved: its image under the archive when it is inside the feature, itself otherwise. */
281
+ function followsFeature(gated, target) {
282
+ return acrossRename(target, gated.featureDir, gated.archiveDest);
283
+ }
284
+ //# sourceMappingURL=links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../../../../../src/commands/archive/plan/document/links.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where every file of the feature will be once this archive lands — the half of\n * the link rewrite that `core/links/parse.ts`'s `rebaseLinks` cannot know.\n *\n * THE DEFECT THIS CLOSES. The requirement merge has re-expressed a delta's\n * relative links from the directory they were written in to the one they land\n * in since the glossary axis shipped. But it re-expressed them against the\n * target's OLD place, and the same archive moves the targets that sit inside\n * the feature: a link from a delta to `features/<FEAT>/glossary/order.md` was\n * rewritten faithfully into the living spec and resolved to nothing one rename\n * later, when the term had been copied to `glossary/order.md` and the feature\n * directory had gone to `features/archive/<FEAT>/`. Every such link was\n * `link.unresolved` on the first `validate --all` after a clean archive — an\n * error the feature gate could not see, because before the archive the link\n * was correct.\n *\n * TWO RULES, and they are the archive's own moves rather than a guess:\n *\n * - A file the archive COPIES into the living tree — a glossary term, a flow,\n * a topology document — is cited at its living copy. That is the document\n * readers are meant to find, and the one `glossary.unlinked` counts a\n * citation of: a link left on the archived copy would resolve and still\n * leave the living term reading as a word nobody uses.\n * - Every other file under the feature directory — its ADRs above all, the\n * intent, the deltas themselves — is cited where the feature now lives,\n * `features/archive/<FEAT>/`. Nothing else copies a feature ADR anywhere: it\n * records a decision taken for this change, and it stays with the change\n * (SCHEMA \"Layout\" says so beside the slot).\n *\n * Built per call from three directory walks — the glossary, the flows and the\n * topology documents, the same walks `../specs.ts` and `../landscape.ts` make\n * to copy them; a `Gated` field would be one more value the gate computes for\n * a phase it does not own.\n *\n * THE OTHER TWO HALVES ARE THE LINKS NO MERGE CARRIES. The feature's own\n * documents (`planFeatureLinks` below) stay in the feature and ride the rename,\n * so their links out of it are one directory short the moment it lands. Every\n * other document in the corpus (`planInboundLinks` below) stays where it is\n * while the files it cites inside the feature move away from under it.\n */\nimport { dirname, resolve } from \"node:path\";\nimport { repoPath } from \"../../../../core/envelope/json.js\";\nimport { movedUnderVouch, parseFrontmatter, stringField } from \"../../../../core/document/frontmatter.js\";\nimport { parseRequirements } from \"../../../../core/document/parse.js\";\nimport { requirementDigest } from \"../../../../core/document/spec.js\";\nimport { featureGlossary, livingTermPath } from \"../../../../core/glossary/delta.js\";\nimport { allAuthoredDocuments, movedMarkdown } from \"../../../../core/links/corpus.js\";\nimport { rebaseLinks } from \"../../../../core/links/parse.js\";\nimport { inOrder } from \"../../../../core/kernel/concurrency.js\";\nimport { acrossRename, isPathInside } from \"../../../../core/kernel/path-safety.js\";\nimport { featuresDir, servicePathsAt } from \"../../../../core/repo/paths.js\";\nimport { listServices } from \"../../../../core/repo/repo.js\";\nimport { featureFlows, livingFlowPath } from \"../../../../core/usecases/delta/flows.js\";\nimport { featureDeployments, livingDeploymentPath } from \"../../../../core/deployment/delta.js\";\nimport { NotUtf8Error, planWrite, readUtf8 } from \"../../../../core/staging/writes.js\";\nimport type { FleetContext } from \"../../../../core/fleet-context.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\nimport type { Gated, Plan } from \"../state.js\";\n\n/** Absolute path in, the absolute path the same file will have after this archive out. */\nexport type Retarget = (target: string) => string;\n\nexport async function featureRetarget(docsDir: DocsDir, gated: Pick<Gated, \"featureDir\" | \"archiveDest\">): Promise<Retarget> {\n const copies = new Map<string, string>();\n for (const term of (await featureGlossary(gated.featureDir)).terms) {\n copies.set(resolve(term.path), resolve(livingTermPath(docsDir, term.id)));\n }\n for (const flow of await featureFlows(gated.featureDir)) {\n copies.set(resolve(flow.path), resolve(livingFlowPath(docsDir, flow.rel)));\n }\n for (const doc of await featureDeployments(gated.featureDir)) {\n copies.set(resolve(doc.path), resolve(livingDeploymentPath(docsDir, doc.rel)));\n }\n return (target: string): string => copies.get(resolve(target)) ?? followsFeature(gated, target);\n}\n\n/**\n * The feature's own markdown documents with their relative links re-expressed\n * for `features/archive/<FEAT>/`, planned at their CURRENT paths so the swap\n * lands before the rename carries them (`../../run.ts`).\n *\n * THE DEFECT THIS CLOSES. The archive is a directory rename, and every document\n * that stays in the feature — the intent, its ADRs, the deltas, a capability\n * delta, the archived copy of a term — kept links written one directory\n * shallower than where they now sit: `../../services/x/spec.md` from\n * `features/<FEAT>/intent.md` names `features/services/x/spec.md` once the\n * intent is `features/archive/<FEAT>/intent.md`. Nothing grades an archived\n * feature (`core/links/corpus.ts` says why), so nothing said so — but the\n * living documents link INTO the archived feature's ADRs (`featureRetarget`\n * above), and a reader who followed one landed on a decision whose own links\n * were all off by one.\n *\n * ONE RULE, the rename's: a target inside the feature moves with it and every\n * other target stays put — `subsystem move`'s rule for its own renames\n * (`commands/subsystem/txn/links.ts`). So a link from the intent to its own\n * ADR or its own glossary term is left exactly as written: the archived\n * record keeps citing the archived copy, which is the text the feature was\n * reviewed against, rather than being redirected to a living copy that will go\n * on changing without it. Only the routes the rename broke are touched, and a\n * link whose text still reaches its target keeps its spelling\n * (`rebaseLinks`).\n *\n * The undo is the snapshot's, like every other write here: each rewritten\n * document is a staged write, so its pre-image is parked and `loam unarchive`\n * puts the authored bytes back (`core/staging/snapshot/snapshot.ts` says where\n * it records them).\n *\n * A document that is not UTF-8 is left exactly as written, and the text\n * output says so — `--json` has no key for it, and minting one for a document\n * nothing grades was not worth a contract change. Every other read here would\n * refuse it, but this one only corrects links, and refusing the archive over\n * such a document would be the larger harm.\n *\n * Read with bounded concurrency and applied in input order (`inOrder`), as\n * `subsystem move`'s rewrite reads its own: a large feature — one\n * `loam migrate-openspec` filled with its legacy documents — is read in full\n * under the lock.\n */\nexport async function planFeatureLinks(\n config: { docsDir: DocsDir },\n gated: Pick<Gated, \"featureDir\" | \"archiveDest\">,\n planned: Plan,\n say: (line?: string) => void,\n): Promise<void> {\n const { docsDir } = config;\n const retarget = (target: string): string => followsFeature(gated, target);\n const read = await inOrder(await movedMarkdown(gated.featureDir), async (path) => {\n try {\n return { path, text: await readUtf8(path) };\n } catch (err) {\n if (!(err instanceof NotUtf8Error)) throw err;\n return { path, text: null };\n }\n });\n const relinked: string[] = [];\n for (const { path, text } of read) {\n const lands = followsFeature(gated, path);\n if (text === null) {\n say(` ⚠ ${repoPath(docsDir, lands)} is not UTF-8 — its links were left as written`);\n continue;\n }\n const next = rebaseLinks(text, { from: dirname(path), to: dirname(lands), retarget });\n if (next === text) continue;\n planned.writes.push(planWrite(path, next));\n relinked.push(repoPath(docsDir, lands));\n }\n if (relinked.length === 0) return;\n say(` feature documents: ${relinked.length} relinked for ${repoPath(docsDir, gated.archiveDest)}/`);\n for (const path of relinked) say(` ${path}`);\n}\n\n/**\n * Every other document in the link corpus whose links reach INTO the feature,\n * re-expressed for where this archive leaves what they cite.\n *\n * THE DEFECT THIS CLOSES. Another change in flight builds on this one's\n * decision (`../FEAT/adrs/0001-x.md` from its intent), or a living ADR cites\n * the decision taken for it while the change is still open. Correct the moment\n * before the archive, so no gate could say otherwise; `link.unresolved` the\n * moment after, because the rename took the directory away from under the\n * link. And no edit the author could make in advance would have helped: the\n * archived path does not exist until the archive does. `subsystem move`\n * rewrites the links into the directories it renames\n * (`commands/subsystem/txn/links.ts`); this is the archive's rename owing the\n * same correction.\n *\n * THE RULE IS `featureRetarget`'s, not the rename's. These documents are not\n * the record of the change; they are the living corpus and the other changes\n * in flight, read against the tree as it is. So a term the archive copies into\n * `glossary/` is cited at the living copy — the one a reader is meant to find\n * and the one `glossary.unlinked` counts — and everything that stays with the\n * change, its ADRs and intent above all, is cited where the feature now lives.\n * Only the routes into the feature change; every other link keeps its\n * spelling (`rebaseLinks`).\n *\n * COMPOSED ONTO THE MERGE. A living spec the merge rewrites can cite the\n * feature in its own narrative, and relinking the bytes on disk would plan a\n * second write of its PRE-merge text — swapped after the merge's, it would\n * undo the merge. So a document this plan already writes is relinked from the\n * text the merge composed, and that one write is replaced in place.\n *\n * THE COSTS ARE SAID THE WAY THE MERGE SAYS ITS OWN. A rewritten route is still\n * a changed byte. Under a vouch stamp it is `content.stale` until the owner\n * re-vouches, so a vouched spec this moves joins `revouchOwed` exactly as one\n * the merge moves does. Inside a living requirement it moves the digest a\n * `Based-On:` pin holds, and the plan names the document. No git refusal of the\n * kind `subsystem move` holds (`move-uncommitted`): archive never stages in\n * git, and its undo is the snapshot — every write here is one of its rows, so\n * `loam unarchive` puts the authored bytes back, a document edited since is\n * `snapshot-stale` like any other, and one whose feature has been archived or\n * removed since is left as it is (`commands/unarchive/restore.ts`).\n *\n * The two notes are the text output's alone — the walk that could not be made\n * and the `Based-On:` cost. `--json` has no key for either: its `warnings` are\n * coherence issues with codes, and a new key for an advisory the next\n * `validate --all` grades in full was not worth a contract change. What an\n * agent acts on is in the payload already: every relinked document is a `plan`\n * row and every re-vouch it owes is on `revouchOwed`.\n */\nexport async function planInboundLinks(\n config: { docsDir: DocsDir; fleet: FleetContext },\n gated: Pick<Gated, \"featureDir\" | \"archiveDest\">,\n planned: Plan,\n say: (line?: string) => void,\n): Promise<void> {\n const { docsDir, fleet } = config;\n let corpus: string[];\n try {\n corpus = await allAuthoredDocuments(docsDir, fleet);\n } catch (err) {\n // The gate enumerated this fleet already, so a walk that fails here is a\n // tree `validate` will diagnose. The archive is not refused over a question\n // it asks to keep the corpus readable — `subsystem move` decides the same —\n // but it does not pretend the question was answered either.\n say(\n ` ⚠ links INTO ${repoPath(docsDir, gated.featureDir)}/ were not checked — the fleet's documents could not be walked ` +\n `(${err instanceof Error ? err.message : String(err)}); \\`loam validate --all\\` names any that broke.`,\n );\n return;\n }\n const retarget = await featureRetarget(docsDir, gated);\n const vouchable = new Map<string, string>();\n for (const service of await listServices(docsDir, fleet)) {\n const paths = servicePathsAt(service.dir);\n vouchable.set(resolve(paths.spec), service.id);\n vouchable.set(resolve(paths.archSpec), service.id);\n }\n const plannedAt = new Map(planned.writes.map((write, at) => [resolve(write.path), { at, write }]));\n const seen = new Set<string>();\n const candidates = corpus.filter((path) => {\n const key = resolve(path);\n if (seen.has(key) || isPathInside(gated.featureDir, path)) return false;\n seen.add(key);\n return true;\n });\n const documents = await inOrder(candidates, async (path) => {\n const composed = plannedAt.get(resolve(path));\n if (composed !== undefined) {\n // A delete or a Buffer is not text this rewrite may compose onto; no\n // markdown planner hands over either.\n const { content } = composed.write;\n return { path, composed, text: typeof content === \"string\" ? content : null };\n }\n try {\n return { path, composed, text: await readUtf8(path) };\n } catch {\n // Absent (the corpus names every slot a service MAY fill) or not UTF-8,\n // which `link.unreadable` already names: nothing loam could write back.\n return { path, composed, text: null };\n }\n });\n const pinned = (md: string): string => parseRequirements(md).map(requirementDigest).join(\",\");\n const relinked: string[] = [];\n const notes: string[] = [];\n for (const { path, composed, text } of documents) {\n if (text === null) continue;\n const next = rebaseLinks(text, { from: dirname(path), to: dirname(path), retarget });\n if (next === text) continue;\n // In place, at the index the merge's write already holds: nothing between\n // here and the map above adds, removes or reorders a planned write.\n if (composed === undefined) planned.writes.push(planWrite(path, next));\n else planned.writes[composed.at] = { ...composed.write, content: next };\n const where = repoPath(docsDir, path);\n relinked.push(where);\n // The merge's own question (`../specs.ts`), asked of the bytes about to be\n // written — and once per document: a spec the merge already named is not\n // owed a second re-vouch for the same commit.\n const service = vouchable.get(resolve(path));\n const fm = parseFrontmatter(next);\n const named = planned.revouchOwed.some((owed) => resolve(owed.path) === resolve(path));\n if (service !== undefined && !named && movedUnderVouch(fm, next)) {\n planned.revouchOwed.push({ service, path, owner: stringField(fm, \"owner\") ?? null });\n }\n // Living documents only: a pin holds the digest of a LIVING requirement,\n // so another feature's own delta text moving pins nothing.\n if (!isPathInside(featuresDir(docsDir), path) && pinned(next) !== pinned(text)) {\n notes.push(\n `${where}: links this archive rewrote sit inside requirements, so a feature in flight that pins one with Based-On: ` +\n \"reports delta.baseline-stale until `loam rebase` re-pins it.\",\n );\n }\n }\n if (relinked.length === 0) return;\n say(` links into the feature: ${relinked.length} document(s) relinked for ${repoPath(docsDir, gated.archiveDest)}/`);\n for (const path of relinked) say(` ${path}`);\n for (const note of notes) say(` note: ${note}`);\n}\n\n/** Where `target` will be once the feature directory has moved: its image under the archive when it is inside the feature, itself otherwise. */\nfunction followsFeature(gated: Pick<Gated, \"featureDir\" | \"archiveDest\">, target: string): string {\n return acrossRename(target, gated.featureDir, gated.archiveDest);\n}\n"]}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * The in-place half of a requirement merge: a living document that already
3
+ * exists has its requirements RUN rewritten, and nothing else.
4
+ *
5
+ * Moved out of `../specs.ts` when that module took on what a CREATED document
6
+ * says (`./head.ts`) and where a merged link points (`./links.ts`): the three
7
+ * are the document-level questions a requirement merge answers, and the merge
8
+ * loop itself is what is left beside the contract and landscape planners.
9
+ */
10
+ import { parseRequirements, splitRequirementsSection } from "../../../../core/document/parse.js";
11
+ import { serializeRequirements } from "../../../../core/document/spec.js";
12
+ /**
13
+ * Rewrite ONLY what the delta touched inside a living spec's requirements run.
14
+ * Byte-for-byte preserved: everything before the first requirement inside
15
+ * `## Requirements` (the intro, the heading line, prose under the heading) and
16
+ * everything from the section's end onward (the next `## ` heading to EOF) —
17
+ * the old cut was a substring `indexOf("\n## Requirements")`, a prefix match
18
+ * that also hit `## Requirements Extra` and silently destroyed every section
19
+ * after the requirements. `living` must be every living requirement — the gate's
20
+ * stray guard refuses any document whose requirements sit outside the section,
21
+ * using the same heading definition, before this output is ever written.
22
+ *
23
+ * Inside the run the unit is one requirement, and an untouched one is written
24
+ * back as its raw slice (`spliceRun`). The whole run used to be re-serialized,
25
+ * and `serializeRequirements` writes a blank line under every
26
+ * `### Requirement:` heading — so a living spec written without one, the shape
27
+ * the adopt brief's own examples taught, had that line inserted under every
28
+ * requirement the feature never mentioned. The diff, the vouch pack and the
29
+ * re-vouch all widened from the delta to the whole section.
30
+ *
31
+ * Exported for `../specs.ts`, which calls it for both corpora, and for
32
+ * test/archive.test.ts: the slice-to-requirement join (`verbatimSlices`) can
33
+ * only be handed a disagreeing pair directly, because the gate refuses every
34
+ * document that would produce one through the CLI.
35
+ */
36
+ export function rewriteRequirementsRun(text, living, merged) {
37
+ const s = splitRequirementsSection(text);
38
+ // No `## Requirements` at all: the stray guard has already refused any doc
39
+ // whose requirements live elsewhere, so this one has none — open the section.
40
+ if (s === null)
41
+ return `${text.trimEnd()}\n\n## Requirements\n\n${serializeRequirements(merged)}`;
42
+ if (s.run !== "")
43
+ return s.head + spliceRun(s, living, merged) + s.tail;
44
+ // The section held no requirements yet: head and tail are raw slices, and the
45
+ // glue supplies the blank lines the author never had reason to write.
46
+ const headGlue = s.head.endsWith("\n\n") ? "" : s.head.endsWith("\n") ? "\n" : "\n\n";
47
+ const tailGlue = s.tail === "" ? "" : "\n";
48
+ return s.head + headGlue + serializeRequirements(merged) + tailGlue + s.tail;
49
+ }
50
+ /** The line breaks (and blank lines) a slice ends with — what separates it from whatever follows. */
51
+ const TRAILING_BREAKS = /(?:\r?\n[ \t]*)*$/;
52
+ /**
53
+ * The merged run: every living requirement the delta left alone as its raw
54
+ * slice, every one it added or replaced serialized, in `merged`'s order.
55
+ *
56
+ * Keyed on object identity, which `applyRequirementDelta` keeps for exactly the
57
+ * requirements it did not touch — so there is no second reading here of what
58
+ * the delta changed, and no way for the splice and the algebra to disagree.
59
+ * Which slice belongs to which living requirement is `verbatimSlices`' join.
60
+ *
61
+ * Prose BETWEEN requirements rides in the slice of the requirement above it,
62
+ * which is where parseRequirements attributes it too: it survives verbatim
63
+ * beside an untouched requirement and goes with a replaced one, as it always
64
+ * did — a MODIFIED carries its full new text.
65
+ */
66
+ function spliceRun(section, living, merged) {
67
+ const verbatim = verbatimSlices(section.requirements, living);
68
+ let out = "";
69
+ let previousVerbatim = true;
70
+ for (const requirement of merged) {
71
+ const raw = verbatim.get(requirement);
72
+ // Two untouched neighbours keep whatever the author put between them. Next
73
+ // to a serialized requirement the canonical blank line is ADDED after the
74
+ // untouched text, never by rewriting it.
75
+ if (out !== "" && (raw === undefined || !previousVerbatim))
76
+ out = withBlankLine(out);
77
+ out += raw ?? serializeRequirements([requirement]);
78
+ previousVerbatim = raw !== undefined;
79
+ }
80
+ if (out === "")
81
+ return "";
82
+ // The run ends as it ended: whatever separated the old last requirement from
83
+ // the next heading (or EOF) now follows the new last one — and at least a
84
+ // newline when a merged requirement lands where the file used to stop without one.
85
+ const ending = section.run.slice(section.run.search(TRAILING_BREAKS));
86
+ const body = out.slice(0, out.search(TRAILING_BREAKS));
87
+ return body + (ending === "" && merged.at(-1) !== living.at(-1) ? "\n" : ending);
88
+ }
89
+ /** A requirement's identity as the merge selects it: `Requirement-ID:` when authored, the exact heading otherwise. */
90
+ const identity = (r) => (r.id === undefined ? `name:${r.name}` : `id:${r.id}`);
91
+ /**
92
+ * Each living requirement's raw slice, joined on its IDENTITY — the slice read
93
+ * back through the same parser that produced `living`, so a slice can only
94
+ * land in place of the requirement it actually is.
95
+ *
96
+ * The join used to be by position, guarded by a count. The two walks agree in
97
+ * order today (one heading-and-fence definition, core/document/parse.ts, and
98
+ * the gate's stray guard refusing any requirement outside the section), but
99
+ * nothing declared that, and equal counts paired differently would have written
100
+ * one requirement's bytes where another belongs — under its vouch stamp, with
101
+ * nothing to say so.
102
+ *
103
+ * Fails closed to the empty map, which serializes every requirement — the old
104
+ * whole-run rewrite, still a correct merge and only a wider diff: when the
105
+ * counts differ, when a slice does not read as exactly one requirement, when
106
+ * two slices claim one identity, or when a living requirement finds no slice
107
+ * of its own.
108
+ */
109
+ function verbatimSlices(slices, living) {
110
+ const none = new Map();
111
+ if (slices.length !== living.length)
112
+ return none;
113
+ const byIdentity = new Map();
114
+ for (const slice of slices) {
115
+ const [only, ...more] = parseRequirements(slice);
116
+ if (only === undefined || more.length > 0 || byIdentity.has(identity(only)))
117
+ return none;
118
+ byIdentity.set(identity(only), slice);
119
+ }
120
+ const verbatim = new Map();
121
+ for (const requirement of living) {
122
+ const slice = byIdentity.get(identity(requirement));
123
+ if (slice === undefined)
124
+ return none;
125
+ // Taken once: a second living requirement of the same identity finds nothing.
126
+ byIdentity.delete(identity(requirement));
127
+ verbatim.set(requirement, slice);
128
+ }
129
+ return verbatim;
130
+ }
131
+ /** `text` ending in a blank line: line breaks appended to make one, nothing rewritten. */
132
+ function withBlankLine(text) {
133
+ const breaks = text.slice(text.search(TRAILING_BREAKS)).split("\n").length - 1;
134
+ return breaks >= 2 ? text : text + "\n".repeat(2 - breaks);
135
+ }
136
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../../../src/commands/archive/plan/document/run.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The in-place half of a requirement merge: a living document that already\n * exists has its requirements RUN rewritten, and nothing else.\n *\n * Moved out of `../specs.ts` when that module took on what a CREATED document\n * says (`./head.ts`) and where a merged link points (`./links.ts`): the three\n * are the document-level questions a requirement merge answers, and the merge\n * loop itself is what is left beside the contract and landscape planners.\n */\nimport { parseRequirements, splitRequirementsSection } from \"../../../../core/document/parse.js\";\nimport { serializeRequirements, type Requirement } from \"../../../../core/document/spec.js\";\n\n/**\n * Rewrite ONLY what the delta touched inside a living spec's requirements run.\n * Byte-for-byte preserved: everything before the first requirement inside\n * `## Requirements` (the intro, the heading line, prose under the heading) and\n * everything from the section's end onward (the next `## ` heading to EOF) —\n * the old cut was a substring `indexOf(\"\\n## Requirements\")`, a prefix match\n * that also hit `## Requirements Extra` and silently destroyed every section\n * after the requirements. `living` must be every living requirement — the gate's\n * stray guard refuses any document whose requirements sit outside the section,\n * using the same heading definition, before this output is ever written.\n *\n * Inside the run the unit is one requirement, and an untouched one is written\n * back as its raw slice (`spliceRun`). The whole run used to be re-serialized,\n * and `serializeRequirements` writes a blank line under every\n * `### Requirement:` heading — so a living spec written without one, the shape\n * the adopt brief's own examples taught, had that line inserted under every\n * requirement the feature never mentioned. The diff, the vouch pack and the\n * re-vouch all widened from the delta to the whole section.\n *\n * Exported for `../specs.ts`, which calls it for both corpora, and for\n * test/archive.test.ts: the slice-to-requirement join (`verbatimSlices`) can\n * only be handed a disagreeing pair directly, because the gate refuses every\n * document that would produce one through the CLI.\n */\nexport function rewriteRequirementsRun(text: string, living: Requirement[], merged: Requirement[]): string {\n const s = splitRequirementsSection(text);\n // No `## Requirements` at all: the stray guard has already refused any doc\n // whose requirements live elsewhere, so this one has none — open the section.\n if (s === null) return `${text.trimEnd()}\\n\\n## Requirements\\n\\n${serializeRequirements(merged)}`;\n if (s.run !== \"\") return s.head + spliceRun(s, living, merged) + s.tail;\n // The section held no requirements yet: head and tail are raw slices, and the\n // glue supplies the blank lines the author never had reason to write.\n const headGlue = s.head.endsWith(\"\\n\\n\") ? \"\" : s.head.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n const tailGlue = s.tail === \"\" ? \"\" : \"\\n\";\n return s.head + headGlue + serializeRequirements(merged) + tailGlue + s.tail;\n}\n\n/** The line breaks (and blank lines) a slice ends with — what separates it from whatever follows. */\nconst TRAILING_BREAKS = /(?:\\r?\\n[ \\t]*)*$/;\n\n/**\n * The merged run: every living requirement the delta left alone as its raw\n * slice, every one it added or replaced serialized, in `merged`'s order.\n *\n * Keyed on object identity, which `applyRequirementDelta` keeps for exactly the\n * requirements it did not touch — so there is no second reading here of what\n * the delta changed, and no way for the splice and the algebra to disagree.\n * Which slice belongs to which living requirement is `verbatimSlices`' join.\n *\n * Prose BETWEEN requirements rides in the slice of the requirement above it,\n * which is where parseRequirements attributes it too: it survives verbatim\n * beside an untouched requirement and goes with a replaced one, as it always\n * did — a MODIFIED carries its full new text.\n */\nfunction spliceRun(\n section: { run: string; requirements: string[] },\n living: Requirement[],\n merged: Requirement[],\n): string {\n const verbatim = verbatimSlices(section.requirements, living);\n let out = \"\";\n let previousVerbatim = true;\n for (const requirement of merged) {\n const raw = verbatim.get(requirement);\n // Two untouched neighbours keep whatever the author put between them. Next\n // to a serialized requirement the canonical blank line is ADDED after the\n // untouched text, never by rewriting it.\n if (out !== \"\" && (raw === undefined || !previousVerbatim)) out = withBlankLine(out);\n out += raw ?? serializeRequirements([requirement]);\n previousVerbatim = raw !== undefined;\n }\n if (out === \"\") return \"\";\n // The run ends as it ended: whatever separated the old last requirement from\n // the next heading (or EOF) now follows the new last one — and at least a\n // newline when a merged requirement lands where the file used to stop without one.\n const ending = section.run.slice(section.run.search(TRAILING_BREAKS));\n const body = out.slice(0, out.search(TRAILING_BREAKS));\n return body + (ending === \"\" && merged.at(-1) !== living.at(-1) ? \"\\n\" : ending);\n}\n\n/** A requirement's identity as the merge selects it: `Requirement-ID:` when authored, the exact heading otherwise. */\nconst identity = (r: Pick<Requirement, \"id\" | \"name\">): string => (r.id === undefined ? `name:${r.name}` : `id:${r.id}`);\n\n/**\n * Each living requirement's raw slice, joined on its IDENTITY — the slice read\n * back through the same parser that produced `living`, so a slice can only\n * land in place of the requirement it actually is.\n *\n * The join used to be by position, guarded by a count. The two walks agree in\n * order today (one heading-and-fence definition, core/document/parse.ts, and\n * the gate's stray guard refusing any requirement outside the section), but\n * nothing declared that, and equal counts paired differently would have written\n * one requirement's bytes where another belongs — under its vouch stamp, with\n * nothing to say so.\n *\n * Fails closed to the empty map, which serializes every requirement — the old\n * whole-run rewrite, still a correct merge and only a wider diff: when the\n * counts differ, when a slice does not read as exactly one requirement, when\n * two slices claim one identity, or when a living requirement finds no slice\n * of its own.\n */\nfunction verbatimSlices(slices: string[], living: Requirement[]): Map<Requirement, string> {\n const none = new Map<Requirement, string>();\n if (slices.length !== living.length) return none;\n const byIdentity = new Map<string, string>();\n for (const slice of slices) {\n const [only, ...more] = parseRequirements(slice);\n if (only === undefined || more.length > 0 || byIdentity.has(identity(only))) return none;\n byIdentity.set(identity(only), slice);\n }\n const verbatim = new Map<Requirement, string>();\n for (const requirement of living) {\n const slice = byIdentity.get(identity(requirement));\n if (slice === undefined) return none;\n // Taken once: a second living requirement of the same identity finds nothing.\n byIdentity.delete(identity(requirement));\n verbatim.set(requirement, slice);\n }\n return verbatim;\n}\n\n/** `text` ending in a blank line: line breaks appended to make one, nothing rewritten. */\nfunction withBlankLine(text: string): string {\n const breaks = text.slice(text.search(TRAILING_BREAKS)).split(\"\\n\").length - 1;\n return breaks >= 2 ? text : text + \"\\n\".repeat(2 - breaks);\n}\n"]}
@@ -20,7 +20,6 @@ import { readUtf8 } from "../../../core/staging/writes.js";
20
20
  import { loadFile } from "../../../core/c4/likec4.js";
21
21
  import { archiveDir as archiveRoot, featurePaths, featureSpecPaths, SPEC_AXES } from "../../../core/repo/paths.js";
22
22
  import { livingCapabilityPaths } from "../../../core/repo/authored/paths.js";
23
- import { featureCapabilityDeltas } from "../../../core/capabilities/delta/tree.js";
24
23
  import { locateServicePaths } from "../../../core/repo/service-target.js";
25
24
  import { featureSpecServices, missingFeatureMessage, resolveFeature } from "../../../core/repo/repo.js";
26
25
  import { featureCoherence } from "../../../core/coherence/coherence.js";
@@ -31,7 +30,7 @@ import { refuseFindings, refuseJson, sayRecovery } from "./refusal.js";
31
30
  import {} from "./state.js";
32
31
  export async function gate(config, featureId, opts, say) {
33
32
  const json = opts.json === true;
34
- const dryRun = opts.dryRun === true;
33
+ // Before anything is read: a commit that was killed between two renames left
35
34
  // the living docs half-written, and every other command in loam would have
36
35
  // planned against them as if they were the truth. Under the same lock, so the
37
36
  // repair cannot race a writer. It refuses when the half-merged files have been
@@ -62,9 +61,7 @@ export async function gate(config, featureId, opts, say) {
62
61
  // no-context path `core/coherence/declared.ts` already documents paying for
63
62
  // the arch delta — so this is not the run's only walk, only the one every
64
63
  // phase downstream of the gate shares.
65
- const capabilityDeltas = (config.fleet === undefined
66
- ? await featureCapabilityDeltas(featureDir)
67
- : await config.fleet.featureCapabilityDeltas(featureDir)).docs;
64
+ const capabilityDeltas = (await config.fleet.featureCapabilityDeltas(featureDir)).docs;
68
65
  // A `specs/<svc>/` whose NAME is not a legal service id, refused before
69
66
  // anything joins that name into a path: the conflict scan below probes
70
67
  // `services/<svc>/<axis>` with it, and the requirements merge would
@@ -104,7 +101,7 @@ export async function gate(config, featureId, opts, say) {
104
101
  // Git conflict markers in a LIVING document this merge would rewrite, before
105
102
  // anything else is asked of it. Both sides of somebody's merge are in the
106
103
  // file, so nothing it says is anyone's text — and the requirements rewrite
107
- // deletes whichever marker lines fall inside the section it owns, turning a
104
+ // deletes whichever marker lines fall inside a requirement it replaces, turning a
108
105
  // conflict anyone can see into a file nobody can tell is wrong. Checked ahead
109
106
  // of the coherence gate for `inspectLandscape`'s reason: a conflicted
110
107
  // document parses, so every verdict downstream of it is an opinion about half
@@ -142,19 +139,23 @@ export async function gate(config, featureId, opts, say) {
142
139
  // the rest. The register is issue.ts's `approveOverrides` — the same data
143
140
  // the `overridable` key in the envelope is resolved from — never a code
144
141
  // string spelled here, because two spellings of "what the flag moves" would
145
- // drift. Today the register holds one code: a `metadata { service }` binding
146
- // the id grammar refuses, anywhere in a tagged element's spliced block. The
147
- // landscape merge would splice that name into the living map verbatim, and
148
- // the new-service scan would probe `services/<binding>/` with it — a '../'
149
- // collapses the probe out of services/ altogether — so the refusal is a
150
- // mechanical fact about the path the name becomes, not a judgment about the
151
- // feature. Same doctrine as the illegal-specs-name refusal above; the dry
152
- // run is gated the same way, and the --json envelope stays not-coherent
153
- // with every issue attached, exactly like the overridable branch below, so
154
- // a consumer branches on the code and not on which refusal path fired.
142
+ // drift. What the register HOLDS has grown: it began as one code (a
143
+ // `metadata { service }` binding the id grammar refuses, where the merge
144
+ // would splice the name into the living map verbatim and probe
145
+ // `services/<binding>/` with it) and now holds the create-only collisions of
146
+ // every whole-file axis besides. They share the doctrine and nothing else —
147
+ // each is a mechanical fact about what the merge would write, never a
148
+ // judgment about the feature — which is why the message below counts them
149
+ // and lets each issue say its own sentence.
150
+ //
151
+ // IT USED TO NAME BINDINGS, and had been wrong for every other code in the
152
+ // register since the use-case slot landed: a feature whose flow collided
153
+ // with a living one was refused with "1 element binding(s) name an illegal
154
+ // service id", about a feature that binds nothing. A refusal headline that
155
+ // describes the wrong breach sends its reader to the wrong file first.
155
156
  const nonOverridable = issues.filter((i) => !approveOverrides(i));
156
157
  if (nonOverridable.length > 0) {
157
- const msg = `archive ${id} — BLOCKED: ${nonOverridable.length} element binding(s) name an illegal service id; --approve does not override this`;
158
+ const msg = `archive ${id} — BLOCKED: ${nonOverridable.length} issue(s) --approve does not override; each names what the merge would write`;
158
159
  if (json) {
159
160
  refuseJson("not-coherent", msg, issues, `features/${dirName}`);
160
161
  return null;
@@ -166,7 +167,9 @@ export async function gate(config, featureId, opts, say) {
166
167
  // line says which of the marks the flag cannot move.
167
168
  for (const i of issues)
168
169
  console.error(` ${SEVERITY_MARK[i.severity]} ${i.message}`);
169
- console.error(`\nOf these, --approve does not override the ${nonOverridable.length} illegal binding(s) — the merge would write a name loam can never resolve into services/, mechanically. Fix the binding(s); the other issues gate as usual.`);
170
+ console.error(`\nOf these, --approve does not override ${nonOverridable.length}: ${[...new Set(nonOverridable.map((i) => i.code))].sort().join(", ")}. ` +
171
+ "Each is a mechanical fact about what the merge would write — a name loam cannot resolve, or a file it would replace wholesale — not a judgement the flag can move. " +
172
+ "Fix those; the other issues gate as usual.");
170
173
  sayExplain("not-coherent");
171
174
  process.exitCode = 1;
172
175
  return null;
@@ -298,7 +301,6 @@ export async function gate(config, featureId, opts, say) {
298
301
  fail(json, "archive-exists", `archive ${id} — BLOCKED: features/archive/${dirName} already exists. Remove or rename it, then re-run.`);
299
302
  return null;
300
303
  }
301
- say(`archive ${id}${dryRun ? " (dry run)" : ""}\n`);
302
304
  return {
303
305
  id,
304
306
  dirName,