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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * WHICH documents loam reads links out of — the corpus, spelled once.
3
+ *
4
+ * Two readers need it and must not disagree. `commands/validate/links/` grades
5
+ * each target's own documents for `link.unresolved`; `core/glossary/backlinks.ts`
6
+ * reads the WHOLE fleet's, because "which documents use this term" is a question
7
+ * no single target can answer. A corpus that differed between them would report
8
+ * a term as unused while the very document citing it was being graded one target
9
+ * over.
10
+ *
11
+ * THE CORPUS IS EVERY AUTHORED MARKDOWN DOCUMENT, and the word doing the work is
12
+ * authored. `AGENTS.md` and the scaffolded `README.md` are excluded because loam
13
+ * WRITES them: a finding against generated prose names a defect its reader
14
+ * cannot fix by editing, since the next `loam init` restores it. Their links are
15
+ * loam's own problem, held on this side of the boundary by
16
+ * `test/docs-facts.test.ts` and `test/package-docs.test.ts`.
17
+ *
18
+ * ADRs AND RUNBOOKS ARE IN IT, and they are the reason the convention was
19
+ * written down at all: an ADR that supersedes another says which, by linking to
20
+ * it. They are also the first documents loam reads that it previously only
21
+ * counted, which is what `link.unreadable` exists for.
22
+ *
23
+ * `delta.likec4` IS NOT, and neither is any YAML: this is a markdown question.
24
+ * The one thing a delta holds that addresses another file — `metadata { op }` —
25
+ * has its own resolution and its own findings.
26
+ */
27
+ import { featureCapabilityDeltas } from "../capabilities/delta/tree.js";
28
+ import { readCapabilityTree } from "../capabilities/tree.js";
29
+ import { featureGlossary } from "../glossary/delta.js";
30
+ import { readGlossary } from "../glossary/tree.js";
31
+ import { featurePaths, featureSpecPaths, fleetAdrsDir, servicePathsAt } from "../repo/paths.js";
32
+ import { capabilityDocsDir, glossaryDir } from "../repo/authored/paths.js";
33
+ import { featureSpecServices, listFeatures, listServices } from "../repo/repo.js";
34
+ import { markdownFiles } from "../repo/tree/fs.js";
35
+ /**
36
+ * One service's authored documents: the two requirement specs, the runbook, and
37
+ * every ADR. The specs cost nothing extra when a `FleetContext` is in play — it
38
+ * has already read both to parse their requirements, and the memo hands back the
39
+ * same text.
40
+ */
41
+ export async function serviceDocuments(paths) {
42
+ return [paths.spec, paths.archSpec, paths.runbook, ...(await markdownFiles(paths.adrsDir))];
43
+ }
44
+ /**
45
+ * One feature's authored documents: the intent, each addressed service's two
46
+ * spec deltas, every ADR, and each capability delta document.
47
+ */
48
+ export async function featureDocuments(featureDir, fleet) {
49
+ const paths = featurePaths(featureDir);
50
+ const docs = [paths.intent, ...(await markdownFiles(paths.adrsDir))];
51
+ for (const svc of await featureSpecServices(featureDir, fleet)) {
52
+ const spec = featureSpecPaths(featureDir, svc);
53
+ docs.push(spec.spec, spec.archSpec);
54
+ }
55
+ const capabilities = fleet === undefined ? await featureCapabilityDeltas(featureDir) : await fleet.featureCapabilityDeltas(featureDir);
56
+ docs.push(...capabilities.docs.map((d) => d.spec));
57
+ // The definitions this feature introduces. Their own links are graded like
58
+ // any other document's — a term that cites a term the feature is not adding
59
+ // is a broken join whether or not it has shipped yet.
60
+ docs.push(...(await featureGlossary(featureDir)).terms.map((t) => t.path));
61
+ return docs;
62
+ }
63
+ /**
64
+ * The documents that belong to no service and no feature: the fleet's own
65
+ * decision records, the living capability tree, and the glossary.
66
+ *
67
+ * Graded at fleet scope, so `validate --all` reports them once. A single-target
68
+ * run says nothing about them — the rule `permissions.unenforced` and
69
+ * `capability.invalid` already follow, and for the same reason: a finding about
70
+ * the fleet repeated on every service target is the report.
71
+ */
72
+ export async function fleetDocuments(docsDir, fleet) {
73
+ const capabilities = fleet === undefined
74
+ ? await readCapabilityTree(capabilityDocsDir(docsDir))
75
+ : (await fleet.capabilities(docsDir)).tree;
76
+ const glossary = await readGlossary(glossaryDir(docsDir));
77
+ return [
78
+ ...(await markdownFiles(fleetAdrsDir(docsDir))),
79
+ ...capabilities.docs.map((d) => d.spec),
80
+ ...glossary.terms.map((t) => t.path),
81
+ ];
82
+ }
83
+ /**
84
+ * Every authored document in the repository — the union of the three above over
85
+ * every service and every in-flight feature.
86
+ *
87
+ * ARCHIVED FEATURES ARE OUT, and the omission is the point rather than an
88
+ * oversight. `features/archive/<FEAT>/` is the evolution history: its documents
89
+ * describe a change that already shipped, its links point at whatever the tree
90
+ * looked like then, and grading them would make every directory rename a
91
+ * retroactive error against a record nobody may edit. `listFeatures` excludes
92
+ * them by default, which is the same judgement one enumeration up.
93
+ */
94
+ export async function allAuthoredDocuments(docsDir, fleet) {
95
+ const docs = await fleetDocuments(docsDir, fleet);
96
+ for (const service of await listServices(docsDir, fleet)) {
97
+ docs.push(...(await serviceDocuments(servicePathsAt(service.dir))));
98
+ }
99
+ for (const feature of await listFeatures(docsDir, {}, fleet)) {
100
+ docs.push(...(await featureDocuments(feature.dir, fleet)));
101
+ }
102
+ return docs;
103
+ }
104
+ //# sourceMappingURL=corpus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus.js","sourceRoot":"","sources":["../../../src/core/links/corpus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WHICH documents loam reads links out of — the corpus, spelled once.\n *\n * Two readers need it and must not disagree. `commands/validate/links/` grades\n * each target's own documents for `link.unresolved`; `core/glossary/backlinks.ts`\n * reads the WHOLE fleet's, because \"which documents use this term\" is a question\n * no single target can answer. A corpus that differed between them would report\n * a term as unused while the very document citing it was being graded one target\n * over.\n *\n * THE CORPUS IS EVERY AUTHORED MARKDOWN DOCUMENT, and the word doing the work is\n * authored. `AGENTS.md` and the scaffolded `README.md` are excluded because loam\n * WRITES them: a finding against generated prose names a defect its reader\n * cannot fix by editing, since the next `loam init` restores it. Their links are\n * loam's own problem, held on this side of the boundary by\n * `test/docs-facts.test.ts` and `test/package-docs.test.ts`.\n *\n * ADRs AND RUNBOOKS ARE IN IT, and they are the reason the convention was\n * written down at all: an ADR that supersedes another says which, by linking to\n * it. They are also the first documents loam reads that it previously only\n * counted, which is what `link.unreadable` exists for.\n *\n * `delta.likec4` IS NOT, and neither is any YAML: this is a markdown question.\n * The one thing a delta holds that addresses another file — `metadata { op }` —\n * has its own resolution and its own findings.\n */\nimport { featureCapabilityDeltas } from \"../capabilities/delta/tree.js\";\nimport { readCapabilityTree } from \"../capabilities/tree.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { featureGlossary } from \"../glossary/delta.js\";\nimport { readGlossary } from \"../glossary/tree.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\nimport { featurePaths, featureSpecPaths, fleetAdrsDir, servicePathsAt, type ServicePaths } from \"../repo/paths.js\";\nimport { capabilityDocsDir, glossaryDir } from \"../repo/authored/paths.js\";\nimport { featureSpecServices, listFeatures, listServices } from \"../repo/repo.js\";\nimport { markdownFiles } from \"../repo/tree/fs.js\";\n\n/**\n * One service's authored documents: the two requirement specs, the runbook, and\n * every ADR. The specs cost nothing extra when a `FleetContext` is in play — it\n * has already read both to parse their requirements, and the memo hands back the\n * same text.\n */\nexport async function serviceDocuments(paths: ServicePaths): Promise<string[]> {\n return [paths.spec, paths.archSpec, paths.runbook, ...(await markdownFiles(paths.adrsDir))];\n}\n\n/**\n * One feature's authored documents: the intent, each addressed service's two\n * spec deltas, every ADR, and each capability delta document.\n */\nexport async function featureDocuments(featureDir: FeatureDir, fleet?: FleetContext): Promise<string[]> {\n const paths = featurePaths(featureDir);\n const docs = [paths.intent, ...(await markdownFiles(paths.adrsDir))];\n for (const svc of await featureSpecServices(featureDir, fleet)) {\n const spec = featureSpecPaths(featureDir, svc);\n docs.push(spec.spec, spec.archSpec);\n }\n const capabilities =\n fleet === undefined ? await featureCapabilityDeltas(featureDir) : await fleet.featureCapabilityDeltas(featureDir);\n docs.push(...capabilities.docs.map((d) => d.spec));\n // The definitions this feature introduces. Their own links are graded like\n // any other document's — a term that cites a term the feature is not adding\n // is a broken join whether or not it has shipped yet.\n docs.push(...(await featureGlossary(featureDir)).terms.map((t) => t.path));\n return docs;\n}\n\n/**\n * The documents that belong to no service and no feature: the fleet's own\n * decision records, the living capability tree, and the glossary.\n *\n * Graded at fleet scope, so `validate --all` reports them once. A single-target\n * run says nothing about them — the rule `permissions.unenforced` and\n * `capability.invalid` already follow, and for the same reason: a finding about\n * the fleet repeated on every service target is the report.\n */\nexport async function fleetDocuments(docsDir: DocsDir, fleet?: FleetContext): Promise<string[]> {\n const capabilities =\n fleet === undefined\n ? await readCapabilityTree(capabilityDocsDir(docsDir))\n : (await fleet.capabilities(docsDir)).tree;\n const glossary = await readGlossary(glossaryDir(docsDir));\n return [\n ...(await markdownFiles(fleetAdrsDir(docsDir))),\n ...capabilities.docs.map((d) => d.spec),\n ...glossary.terms.map((t) => t.path),\n ];\n}\n\n/**\n * Every authored document in the repository — the union of the three above over\n * every service and every in-flight feature.\n *\n * ARCHIVED FEATURES ARE OUT, and the omission is the point rather than an\n * oversight. `features/archive/<FEAT>/` is the evolution history: its documents\n * describe a change that already shipped, its links point at whatever the tree\n * looked like then, and grading them would make every directory rename a\n * retroactive error against a record nobody may edit. `listFeatures` excludes\n * them by default, which is the same judgement one enumeration up.\n */\nexport async function allAuthoredDocuments(docsDir: DocsDir, fleet?: FleetContext): Promise<string[]> {\n const docs = await fleetDocuments(docsDir, fleet);\n for (const service of await listServices(docsDir, fleet)) {\n docs.push(...(await serviceDocuments(servicePathsAt(service.dir))));\n }\n for (const feature of await listFeatures(docsDir, {}, fleet)) {\n docs.push(...(await featureDocuments(feature.dir, fleet)));\n }\n return docs;\n}\n"]}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * `link.unresolved` — a markdown link in an authored document whose target is
3
+ * not there, or is there under a different spelling.
4
+ *
5
+ * The convention this grades has been written down since the fleet-ADR work and
6
+ * said, in both `SCHEMA.md` and the generated `AGENTS.md`, that nothing
7
+ * validated it. This module is what makes that sentence false, and the reason
8
+ * the convention was chosen is the reason it can: the target of a standard
9
+ * markdown link is a real relative path, so "does this resolve" has a
10
+ * filesystem answer, where a wikilink would need Obsidian's
11
+ * shortest-unique-path search reimplemented — guessing, which loam does not do.
12
+ *
13
+ * ERROR, and the severity is the decision the maintainer took against the
14
+ * alternative of warning outside `glossary/`. A link is a join of the same kind
15
+ * as `Operations:` and `Covers:`, both of which error when they resolve to
16
+ * nothing; the fact that this one is written in prose changes who typed it, not
17
+ * whether it points at something. A fleet adopting loam with rotted links sees
18
+ * them on its first run — a user-visible widening of the same class as the two
19
+ * the use-case and business axes shipped, and recorded in CHANGELOG as one.
20
+ *
21
+ * ONE FINDING PER DOCUMENT, with every broken link in `details`. A document
22
+ * whose links have rotted is one editing session and one reviewer, not ten
23
+ * separate breaches, and per-link findings would let a single stale directory
24
+ * rename fill a `validate --all` report with the same fix restated.
25
+ *
26
+ * THE RESOLUTION IS EXPORTED SEPARATELY from the finding, because a second
27
+ * reader needs the other half of the answer: the glossary grades a term by who
28
+ * links TO it, which is this same walk read forwards instead of backwards.
29
+ */
30
+ import { existsSync } from "node:fs";
31
+ import { basename, dirname, resolve } from "node:path";
32
+ import { repoPath } from "../envelope/json.js";
33
+ import { isPathInside } from "../kernel/path-safety.js";
34
+ import { pathCaseIndex } from "./case.js";
35
+ import { documentLinks } from "./parse.js";
36
+ /**
37
+ * Every link in one document, resolved against the docs repo.
38
+ *
39
+ * TARGETS OUTSIDE THE DOCS REPO ARE NEITHER RESOLVED NOR BROKEN, and the
40
+ * exclusion is honesty rather than leniency. A docs repo is routinely checked
41
+ * out beside nothing else, so `../../payments-svc/README.md` is a path loam
42
+ * cannot answer for: the file's absence here is evidence about this checkout,
43
+ * not about the link. Only a target that stays inside `docsDir` gets a verdict,
44
+ * which is the same boundary every other reader in loam observes and the reason
45
+ * the finding below can afford to be an error at all.
46
+ *
47
+ * CASE COUNTS, and it must, because the alternative is a check that passes on
48
+ * the author's machine and fails where the work is read. `existsSync` is
49
+ * case-insensitive on Windows and macOS; GitHub's renderer and every Linux CI
50
+ * runner are not. So a resolving link is one that exists AND is spelled the way
51
+ * the filesystem stores it — and when it is not, `storedAs` carries the real
52
+ * name into the message, on every platform alike, so the diagnosis does not
53
+ * depend on which operating system ran the check.
54
+ */
55
+ export function resolveLinks(doc, scope) {
56
+ const cases = scope.cases ?? pathCaseIndex();
57
+ const dir = dirname(doc.path);
58
+ const linkage = { resolved: [], broken: [] };
59
+ for (const link of documentLinks(doc.text)) {
60
+ const target = resolve(dir, link.path);
61
+ if (!isPathInside(scope.docsDir, target))
62
+ continue;
63
+ // The overlay is checked FIRST and without the spelling test: the file is
64
+ // not on disk, so there is no stored name to compare against, and the id it
65
+ // came from was a `readdir` entry in the feature's own tree.
66
+ if (scope.overlay?.has(resolve(target)) === true) {
67
+ linkage.resolved.push({ link, target });
68
+ continue;
69
+ }
70
+ if (existsSync(target) && cases.spelledExactly(scope.docsDir, target)) {
71
+ linkage.resolved.push({ link, target });
72
+ continue;
73
+ }
74
+ const storedAs = cases.storedAs(dirname(target), basename(target));
75
+ linkage.broken.push({ link, ...(storedAs === undefined ? {} : { storedAs }) });
76
+ }
77
+ return linkage;
78
+ }
79
+ /** The links in one document that resolve to nothing, as at most one finding. */
80
+ export function unresolvedLinkFindings(doc, scope) {
81
+ const where = repoPath(scope.docsDir, doc.path);
82
+ const { broken } = resolveLinks(doc, scope);
83
+ if (broken.length === 0)
84
+ return [];
85
+ return [
86
+ {
87
+ severity: "error",
88
+ code: "link.unresolved",
89
+ ...(scope.subject === undefined ? {} : { subject: scope.subject }),
90
+ message: `${where}: ${broken.length} markdown link(s) resolve to nothing. ` +
91
+ "A link between documents here is a join — fix the path, or write the document it points at. " +
92
+ "Targets outside the docs repo and links to a heading (`#section`) are not graded.",
93
+ details: broken.map((one) => detail(where, one)),
94
+ },
95
+ ];
96
+ }
97
+ /**
98
+ * One broken link, spelled so the author can find it: the file and line first,
99
+ * then the link exactly as written. `raw` rather than the decoded path, because
100
+ * the decoded form is not the string that is in the document.
101
+ *
102
+ * The stored spelling is appended when the only thing wrong is case, because
103
+ * without it the message reads as a lie — the file the author is being told to
104
+ * write is already sitting beside the one they linked from.
105
+ */
106
+ function detail(where, one) {
107
+ const line = `${where}:${one.link.line}: [${one.link.text}](${one.link.raw})`;
108
+ return one.storedAs === undefined ? line : `${line} — stored as '${one.storedAs}'`;
109
+ }
110
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../../../src/core/links/findings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `link.unresolved` — a markdown link in an authored document whose target is\n * not there, or is there under a different spelling.\n *\n * The convention this grades has been written down since the fleet-ADR work and\n * said, in both `SCHEMA.md` and the generated `AGENTS.md`, that nothing\n * validated it. This module is what makes that sentence false, and the reason\n * the convention was chosen is the reason it can: the target of a standard\n * markdown link is a real relative path, so \"does this resolve\" has a\n * filesystem answer, where a wikilink would need Obsidian's\n * shortest-unique-path search reimplemented — guessing, which loam does not do.\n *\n * ERROR, and the severity is the decision the maintainer took against the\n * alternative of warning outside `glossary/`. A link is a join of the same kind\n * as `Operations:` and `Covers:`, both of which error when they resolve to\n * nothing; the fact that this one is written in prose changes who typed it, not\n * whether it points at something. A fleet adopting loam with rotted links sees\n * them on its first run — a user-visible widening of the same class as the two\n * the use-case and business axes shipped, and recorded in CHANGELOG as one.\n *\n * ONE FINDING PER DOCUMENT, with every broken link in `details`. A document\n * whose links have rotted is one editing session and one reviewer, not ten\n * separate breaches, and per-link findings would let a single stale directory\n * rename fill a `validate --all` report with the same fix restated.\n *\n * THE RESOLUTION IS EXPORTED SEPARATELY from the finding, because a second\n * reader needs the other half of the answer: the glossary grades a term by who\n * links TO it, which is this same walk read forwards instead of backwards.\n */\nimport { existsSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { repoPath } from \"../envelope/json.js\";\nimport { isPathInside } from \"../kernel/path-safety.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\nimport { pathCaseIndex, type PathCaseIndex } from \"./case.js\";\nimport { documentLinks, type DocumentLink } from \"./parse.js\";\n\n/** One authored document, and the text somebody already read out of it. */\nexport interface LinkedDocument {\n /** Absolute path of the document — the directory every relative target resolves against. */\n path: string;\n text: string;\n}\n\n/** What the finding is filed under, and the tree it may look inside. */\nexport interface LinkScope {\n docsDir: DocsDir;\n /** The service or feature the report is about; absent at fleet scope. */\n subject?: string;\n /**\n * The directory-listing memo behind the case check, shared across a whole\n * run by whoever owns the walk. Omitted, each call builds its own — correct,\n * just not shared. Never a module-level cache: a listing that outlived one\n * command would answer for a directory that has since changed.\n */\n cases?: PathCaseIndex;\n /**\n * Absolute paths that do not exist yet but WILL once an in-flight feature\n * archives — today, the glossary terms that feature introduces.\n *\n * A feature cites the word it is adding at the word's FUTURE living path,\n * because that is where the link has to point once the change lands; citing\n * the delta's own path would resolve now and rot on the day it ships. Without\n * this set, the axis would refuse its own headline case — which is exactly\n * the overlay the capability axis had to add to `Realizes:` after shipping\n * without one.\n */\n overlay?: ReadonlySet<string>;\n}\n\n/** A link that points at a real file inside the docs repo, and the file it points at. */\nexport interface ResolvedLink {\n link: DocumentLink;\n /** The absolute path the link names, as `node:path` resolves it. */\n target: string;\n}\n\n/** One document's links, split by whether the docs repo answers for them. */\nexport interface DocumentLinkage {\n resolved: ResolvedLink[];\n /** Links inside the docs repo that name nothing — with the stored spelling, where there is one. */\n broken: Array<{ link: DocumentLink; storedAs?: string }>;\n}\n\n/**\n * Every link in one document, resolved against the docs repo.\n *\n * TARGETS OUTSIDE THE DOCS REPO ARE NEITHER RESOLVED NOR BROKEN, and the\n * exclusion is honesty rather than leniency. A docs repo is routinely checked\n * out beside nothing else, so `../../payments-svc/README.md` is a path loam\n * cannot answer for: the file's absence here is evidence about this checkout,\n * not about the link. Only a target that stays inside `docsDir` gets a verdict,\n * which is the same boundary every other reader in loam observes and the reason\n * the finding below can afford to be an error at all.\n *\n * CASE COUNTS, and it must, because the alternative is a check that passes on\n * the author's machine and fails where the work is read. `existsSync` is\n * case-insensitive on Windows and macOS; GitHub's renderer and every Linux CI\n * runner are not. So a resolving link is one that exists AND is spelled the way\n * the filesystem stores it — and when it is not, `storedAs` carries the real\n * name into the message, on every platform alike, so the diagnosis does not\n * depend on which operating system ran the check.\n */\nexport function resolveLinks(doc: LinkedDocument, scope: LinkScope): DocumentLinkage {\n const cases = scope.cases ?? pathCaseIndex();\n const dir = dirname(doc.path);\n const linkage: DocumentLinkage = { resolved: [], broken: [] };\n for (const link of documentLinks(doc.text)) {\n const target = resolve(dir, link.path);\n if (!isPathInside(scope.docsDir, target)) continue;\n // The overlay is checked FIRST and without the spelling test: the file is\n // not on disk, so there is no stored name to compare against, and the id it\n // came from was a `readdir` entry in the feature's own tree.\n if (scope.overlay?.has(resolve(target)) === true) {\n linkage.resolved.push({ link, target });\n continue;\n }\n if (existsSync(target) && cases.spelledExactly(scope.docsDir, target)) {\n linkage.resolved.push({ link, target });\n continue;\n }\n const storedAs = cases.storedAs(dirname(target), basename(target));\n linkage.broken.push({ link, ...(storedAs === undefined ? {} : { storedAs }) });\n }\n return linkage;\n}\n\n/** The links in one document that resolve to nothing, as at most one finding. */\nexport function unresolvedLinkFindings(doc: LinkedDocument, scope: LinkScope): Finding[] {\n const where = repoPath(scope.docsDir, doc.path);\n const { broken } = resolveLinks(doc, scope);\n if (broken.length === 0) return [];\n return [\n {\n severity: \"error\",\n code: \"link.unresolved\",\n ...(scope.subject === undefined ? {} : { subject: scope.subject }),\n message:\n `${where}: ${broken.length} markdown link(s) resolve to nothing. ` +\n \"A link between documents here is a join — fix the path, or write the document it points at. \" +\n \"Targets outside the docs repo and links to a heading (`#section`) are not graded.\",\n details: broken.map((one) => detail(where, one)),\n },\n ];\n}\n\n/**\n * One broken link, spelled so the author can find it: the file and line first,\n * then the link exactly as written. `raw` rather than the decoded path, because\n * the decoded form is not the string that is in the document.\n *\n * The stored spelling is appended when the only thing wrong is case, because\n * without it the message reads as a lie — the file the author is being told to\n * write is already sitting beside the one they linked from.\n */\nfunction detail(where: string, one: { link: DocumentLink; storedAs?: string }): string {\n const line = `${where}:${one.link.line}: [${one.link.text}](${one.link.raw})`;\n return one.storedAs === undefined ? line : `${line} — stored as '${one.storedAs}'`;\n}\n"]}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Every markdown link a document writes, as data.
3
+ *
4
+ * A link between documents in a docs repo is a JOIN, not decoration — a
5
+ * requirement that names a domain term points at that term's definition, and an
6
+ * ADR that supersedes another says which. `SCHEMA.md` and the generated
7
+ * `AGENTS.md` have stated the convention (standard markdown links, relative
8
+ * path targets) since the fleet-ADR work; this module is what finally reads
9
+ * one, so `./findings.ts` can answer the only question the convention was
10
+ * chosen for: does the target exist.
11
+ *
12
+ * WHY A PARSER RATHER THAN A REGEX AT THE CALL SITE. Two classes of false
13
+ * positive make the naive `\[.*\]\(.*\)` unusable on the documents loam owns,
14
+ * and both occur in loam's OWN generated prose. A fenced block is where a
15
+ * document SHOWS the convention — the `AGENTS.md` section that teaches the link
16
+ * format contains a link nobody wrote to be followed — and an inline code span
17
+ * is where prose quotes a path. Both must read as text. The fence rule is the
18
+ * one `core/document/parse.ts` already applies to headings, and it is imported
19
+ * rather than restated for the reason that module gives about its own sampler:
20
+ * two readers disagreeing about what a fence encloses prescribe a document no
21
+ * author can satisfy.
22
+ */
23
+ import { relative, resolve, sep } from "node:path";
24
+ import { fenceTracker } from "../kernel/fences.js";
25
+ /**
26
+ * Inline links and images: `[text](target)`, `![alt](target)`,
27
+ * `[text](<target with spaces>)`, `[text](target "title")`.
28
+ *
29
+ * The target alternation takes `<...>` first so an angle-bracketed path
30
+ * containing spaces stays one token; the bare form stops at whitespace or `)`,
31
+ * which gives up on the balanced-parens spelling markdown also allows
32
+ * (`[x](a(1).md)`). That is a deliberate under-read: it costs a link nobody
33
+ * writes, where over-reading would swallow the rest of a sentence and report a
34
+ * target no author would recognise.
35
+ */
36
+ const INLINE_RE = /!?\[([^\]]*)\]\(\s*(<[^>\n]*>|[^\s)]*)(?:\s+(?:"[^"]*"|'[^']*'|\([^)]*\)))?\s*\)/gd;
37
+ /**
38
+ * A reference DEFINITION — `[label]: target "title"` on its own line. Included
39
+ * because the target is the same join written in a different place: a document
40
+ * that collects its links at the bottom is following the convention, and a
41
+ * reader that only saw inline links would grade it as having none.
42
+ */
43
+ const DEFINITION_RE = /^ {0,3}\[([^\]]+)\]:\s*(<[^>\n]*>|\S+)/d;
44
+ /**
45
+ * Code spans, blanked before matching. `\`[a](b)\`` is prose ABOUT a link and
46
+ * resolving it would convict a document for explaining the convention. Blanked
47
+ * to spaces rather than removed so every column after it — and therefore any
48
+ * real link later on the same line — keeps its position.
49
+ */
50
+ const CODE_SPAN_RE = /(`+)(?:[^`]|(?!\1)`)*\1/g;
51
+ /**
52
+ * Every link in `md`, in document order.
53
+ *
54
+ * The whole document is scanned, frontmatter included: a YAML `see: [x](y.md)`
55
+ * is not a link loam wrote a field for, and the alternative — teaching this
56
+ * module where frontmatter ends — would make it read the one part of a document
57
+ * loam's own doctrine says is structured. A link that resolves is a link that
58
+ * resolves wherever it is written.
59
+ */
60
+ export function documentLinks(md) {
61
+ const fenced = fenceTracker();
62
+ const out = [];
63
+ md.split(/\r?\n/).forEach((rawLine, i) => {
64
+ if (fenced(rawLine))
65
+ return;
66
+ const line = rawLine.replace(CODE_SPAN_RE, (m) => " ".repeat(m.length));
67
+ const definition = DEFINITION_RE.exec(line);
68
+ if (definition) {
69
+ push(out, definition[2], definition[1], i + 1);
70
+ return;
71
+ }
72
+ INLINE_RE.lastIndex = 0;
73
+ for (let m = INLINE_RE.exec(line); m !== null; m = INLINE_RE.exec(line)) {
74
+ push(out, m[2], m[1], i + 1);
75
+ }
76
+ });
77
+ return out;
78
+ }
79
+ /**
80
+ * Record one link, unless its target is not a path this repository can answer
81
+ * for. Four exclusions, each of them a question with no filesystem answer
82
+ * rather than a link loam declines to grade:
83
+ *
84
+ * - an EMPTY target (`[text]()`) addresses nothing;
85
+ * - a SCHEME (`https:`, `mailto:`, and a Windows `C:` drive with it) addresses
86
+ * something outside this repository;
87
+ * - a ROOT-relative or protocol-relative target (`/x`, `//host/x`) is resolved
88
+ * by whatever renders the document — a site root, not the file's directory —
89
+ * so loam does not know what it points at;
90
+ * - a PURE fragment (`#section`) addresses this same document, and grading it
91
+ * means resolving heading anchors, which is a different question from "does
92
+ * this file exist".
93
+ *
94
+ * The last two are DEFENSIVE rather than decisive, and a mutation run says so:
95
+ * removing either leaves every test green, because a pure fragment leaves an
96
+ * empty path and a site-root target resolves outside the docs repo, where
97
+ * `./findings.ts` declines to answer anyway. They are kept because the reason
98
+ * differs from that boundary's — loam is not silent about these because they
99
+ * are elsewhere, it is silent because it does not know where they are — and a
100
+ * later reader deleting them as dead would be removing an intention, not a
101
+ * branch.
102
+ */
103
+ function push(out, target, text, line) {
104
+ const raw = target.startsWith("<") && target.endsWith(">") ? target.slice(1, -1) : target;
105
+ if (raw === "" || raw.startsWith("#") || raw.startsWith("/"))
106
+ return;
107
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(raw))
108
+ return;
109
+ const path = decodePath(raw.split("#")[0]);
110
+ if (path === "")
111
+ return;
112
+ out.push({ raw, path, text, line });
113
+ }
114
+ /**
115
+ * Percent-decode a link target, or keep it verbatim when it does not decode.
116
+ *
117
+ * `[Order line](order%20line.md)` is how every editor writes a path holding a
118
+ * space, so a resolver that skipped this step would report the one spelling
119
+ * authors do not type by hand. `decodeURIComponent` throws on a lone `%` — a
120
+ * literal percent in a filename is legal and rarer than the escape — and the
121
+ * honest answer there is the string as written, which then resolves or does not
122
+ * on its own terms.
123
+ */
124
+ function decodePath(raw) {
125
+ try {
126
+ return decodeURIComponent(raw);
127
+ }
128
+ catch {
129
+ return raw;
130
+ }
131
+ }
132
+ /**
133
+ * Re-express every relative link in `md` from a new base directory — the merge's
134
+ * half of the link convention.
135
+ *
136
+ * THE DEFECT THIS CLOSES is structural and predates the glossary. A feature's
137
+ * `specs/<svc>/spec.md` sits four directories below the docs root; the living
138
+ * `services/<svc>/spec.md` it merges into sits two. `loam archive` copies a
139
+ * requirement's text verbatim, so `[Order](../../../../glossary/order.md)` —
140
+ * correct where it was written, and clickable in the pull request that reviewed
141
+ * it — lands in the living document pointing two directories above the
142
+ * repository. The same is true of a delta requirement citing an ADR, which has
143
+ * been possible since before this module existed.
144
+ *
145
+ * REWRITING IS THE ONLY ANSWER THAT KEEPS BOTH PROPERTIES. The alternative
146
+ * considered was to have authors write the link relative to where the text will
147
+ * END UP, leaving loam to touch nothing — but that link does not resolve in the
148
+ * delta, and "it renders as a link in pull-request review, which is where these
149
+ * documents are actually read" is the convention's own first argument. So the
150
+ * author writes what is true where they write it, and the merge makes it true
151
+ * where it lands. The target is unchanged: only the route to it is.
152
+ *
153
+ * It is a positional rewrite, never a re-render: the matched target's own span
154
+ * is replaced and every other byte of the line — the link text, the title, the
155
+ * prose around it — is left exactly as written.
156
+ */
157
+ export function rebaseLinks(md, move) {
158
+ const from = resolve(move.from);
159
+ const to = resolve(move.to);
160
+ if (from === to)
161
+ return md;
162
+ const fenced = fenceTracker();
163
+ // `split` with a capturing group keeps the separators, so a document's own
164
+ // mixture of CRLF and LF survives a rewrite that touches neither.
165
+ const parts = md.split(/(\r?\n)/);
166
+ for (let i = 0; i < parts.length; i += 2) {
167
+ const rawLine = parts[i];
168
+ if (fenced(rawLine))
169
+ continue;
170
+ parts[i] = rebaseLine(rawLine, from, to);
171
+ }
172
+ return parts.join("");
173
+ }
174
+ /** One line's links re-expressed, right to left so earlier spans keep their offsets. */
175
+ function rebaseLine(rawLine, from, to) {
176
+ const blanked = rawLine.replace(CODE_SPAN_RE, (m) => " ".repeat(m.length));
177
+ const spans = [];
178
+ const collect = (m) => {
179
+ const at = m.indices?.[2];
180
+ if (at === undefined)
181
+ return;
182
+ const rebased = rebaseTarget(m[2], from, to);
183
+ if (rebased !== null)
184
+ spans.push([at[0], at[1], rebased]);
185
+ };
186
+ const definition = DEFINITION_RE.exec(blanked);
187
+ if (definition)
188
+ collect(definition);
189
+ else {
190
+ INLINE_RE.lastIndex = 0;
191
+ for (let m = INLINE_RE.exec(blanked); m !== null; m = INLINE_RE.exec(blanked))
192
+ collect(m);
193
+ }
194
+ let out = rawLine;
195
+ for (const [start, end, replacement] of spans.reverse()) {
196
+ out = out.slice(0, start) + replacement + out.slice(end);
197
+ }
198
+ return out;
199
+ }
200
+ /**
201
+ * One target, re-expressed from `to` instead of `from`, or null when there is
202
+ * nothing to re-express.
203
+ *
204
+ * The exclusions are `push`'s exactly — an absolute URL, a site-root path and a
205
+ * bare fragment mean the same thing from every directory — and the ANGLE-BRACKET
206
+ * and PERCENT-ESCAPE forms are preserved rather than normalised: a rewrite that
207
+ * silently changed `<a b.md>` into `a%20b.md` would be editing the author's
208
+ * spelling under cover of moving their file.
209
+ */
210
+ function rebaseTarget(target, from, to) {
211
+ const angled = target.startsWith("<") && target.endsWith(">");
212
+ const raw = angled ? target.slice(1, -1) : target;
213
+ if (raw === "" || raw.startsWith("#") || raw.startsWith("/"))
214
+ return null;
215
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(raw))
216
+ return null;
217
+ const hash = raw.indexOf("#");
218
+ const pathPart = hash === -1 ? raw : raw.slice(0, hash);
219
+ const fragment = hash === -1 ? "" : raw.slice(hash);
220
+ if (pathPart === "")
221
+ return null;
222
+ const encoded = pathPart.includes("%");
223
+ let rebased = relative(to, resolve(from, decodePath(pathPart))).split(sep).join("/");
224
+ // `relative` returns a bare name for a sibling, which markdown reads as a
225
+ // path relative to the file anyway — but `./` is what an author writes and
226
+ // what keeps the link distinguishable from a reference label.
227
+ if (rebased !== "" && !rebased.startsWith("."))
228
+ rebased = `./${rebased}`;
229
+ if (rebased === "")
230
+ return null;
231
+ if (!angled && (encoded || rebased.includes(" ")))
232
+ rebased = rebased.split(" ").join("%20");
233
+ const rewritten = rebased + fragment;
234
+ return angled ? `<${rewritten}>` : rewritten;
235
+ }
236
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/core/links/parse.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Every markdown link a document writes, as data.\n *\n * A link between documents in a docs repo is a JOIN, not decoration — a\n * requirement that names a domain term points at that term's definition, and an\n * ADR that supersedes another says which. `SCHEMA.md` and the generated\n * `AGENTS.md` have stated the convention (standard markdown links, relative\n * path targets) since the fleet-ADR work; this module is what finally reads\n * one, so `./findings.ts` can answer the only question the convention was\n * chosen for: does the target exist.\n *\n * WHY A PARSER RATHER THAN A REGEX AT THE CALL SITE. Two classes of false\n * positive make the naive `\\[.*\\]\\(.*\\)` unusable on the documents loam owns,\n * and both occur in loam's OWN generated prose. A fenced block is where a\n * document SHOWS the convention — the `AGENTS.md` section that teaches the link\n * format contains a link nobody wrote to be followed — and an inline code span\n * is where prose quotes a path. Both must read as text. The fence rule is the\n * one `core/document/parse.ts` already applies to headings, and it is imported\n * rather than restated for the reason that module gives about its own sampler:\n * two readers disagreeing about what a fence encloses prescribe a document no\n * author can satisfy.\n */\nimport { relative, resolve, sep } from \"node:path\";\nimport { fenceTracker } from \"../kernel/fences.js\";\n\n/** One link written in a markdown document. */\nexport interface DocumentLink {\n /** The target exactly as written, angle brackets stripped, fragment included. */\n raw: string;\n /**\n * The FILE half — `raw` with any `#fragment` removed and percent-escapes\n * decoded. This is what resolves against the filesystem; `raw` is what the\n * finding quotes back, because that is the string the author has to find.\n */\n path: string;\n /** The link text, or the reference label for a definition. Quoted in the finding. */\n text: string;\n /** 1-based line the link was written on. */\n line: number;\n}\n\n/**\n * Inline links and images: `[text](target)`, `![alt](target)`,\n * `[text](<target with spaces>)`, `[text](target \"title\")`.\n *\n * The target alternation takes `<...>` first so an angle-bracketed path\n * containing spaces stays one token; the bare form stops at whitespace or `)`,\n * which gives up on the balanced-parens spelling markdown also allows\n * (`[x](a(1).md)`). That is a deliberate under-read: it costs a link nobody\n * writes, where over-reading would swallow the rest of a sentence and report a\n * target no author would recognise.\n */\nconst INLINE_RE = /!?\\[([^\\]]*)\\]\\(\\s*(<[^>\\n]*>|[^\\s)]*)(?:\\s+(?:\"[^\"]*\"|'[^']*'|\\([^)]*\\)))?\\s*\\)/gd;\n\n/**\n * A reference DEFINITION — `[label]: target \"title\"` on its own line. Included\n * because the target is the same join written in a different place: a document\n * that collects its links at the bottom is following the convention, and a\n * reader that only saw inline links would grade it as having none.\n */\nconst DEFINITION_RE = /^ {0,3}\\[([^\\]]+)\\]:\\s*(<[^>\\n]*>|\\S+)/d;\n\n/**\n * Code spans, blanked before matching. `\\`[a](b)\\`` is prose ABOUT a link and\n * resolving it would convict a document for explaining the convention. Blanked\n * to spaces rather than removed so every column after it — and therefore any\n * real link later on the same line — keeps its position.\n */\nconst CODE_SPAN_RE = /(`+)(?:[^`]|(?!\\1)`)*\\1/g;\n\n/**\n * Every link in `md`, in document order.\n *\n * The whole document is scanned, frontmatter included: a YAML `see: [x](y.md)`\n * is not a link loam wrote a field for, and the alternative — teaching this\n * module where frontmatter ends — would make it read the one part of a document\n * loam's own doctrine says is structured. A link that resolves is a link that\n * resolves wherever it is written.\n */\nexport function documentLinks(md: string): DocumentLink[] {\n const fenced = fenceTracker();\n const out: DocumentLink[] = [];\n md.split(/\\r?\\n/).forEach((rawLine, i) => {\n if (fenced(rawLine)) return;\n const line = rawLine.replace(CODE_SPAN_RE, (m) => \" \".repeat(m.length));\n const definition = DEFINITION_RE.exec(line);\n if (definition) {\n push(out, definition[2]!, definition[1]!, i + 1);\n return;\n }\n INLINE_RE.lastIndex = 0;\n for (let m = INLINE_RE.exec(line); m !== null; m = INLINE_RE.exec(line)) {\n push(out, m[2]!, m[1]!, i + 1);\n }\n });\n return out;\n}\n\n/**\n * Record one link, unless its target is not a path this repository can answer\n * for. Four exclusions, each of them a question with no filesystem answer\n * rather than a link loam declines to grade:\n *\n * - an EMPTY target (`[text]()`) addresses nothing;\n * - a SCHEME (`https:`, `mailto:`, and a Windows `C:` drive with it) addresses\n * something outside this repository;\n * - a ROOT-relative or protocol-relative target (`/x`, `//host/x`) is resolved\n * by whatever renders the document — a site root, not the file's directory —\n * so loam does not know what it points at;\n * - a PURE fragment (`#section`) addresses this same document, and grading it\n * means resolving heading anchors, which is a different question from \"does\n * this file exist\".\n *\n * The last two are DEFENSIVE rather than decisive, and a mutation run says so:\n * removing either leaves every test green, because a pure fragment leaves an\n * empty path and a site-root target resolves outside the docs repo, where\n * `./findings.ts` declines to answer anyway. They are kept because the reason\n * differs from that boundary's — loam is not silent about these because they\n * are elsewhere, it is silent because it does not know where they are — and a\n * later reader deleting them as dead would be removing an intention, not a\n * branch.\n */\nfunction push(out: DocumentLink[], target: string, text: string, line: number): void {\n const raw = target.startsWith(\"<\") && target.endsWith(\">\") ? target.slice(1, -1) : target;\n if (raw === \"\" || raw.startsWith(\"#\") || raw.startsWith(\"/\")) return;\n if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(raw)) return;\n const path = decodePath(raw.split(\"#\")[0]!);\n if (path === \"\") return;\n out.push({ raw, path, text, line });\n}\n\n/**\n * Percent-decode a link target, or keep it verbatim when it does not decode.\n *\n * `[Order line](order%20line.md)` is how every editor writes a path holding a\n * space, so a resolver that skipped this step would report the one spelling\n * authors do not type by hand. `decodeURIComponent` throws on a lone `%` — a\n * literal percent in a filename is legal and rarer than the escape — and the\n * honest answer there is the string as written, which then resolves or does not\n * on its own terms.\n */\nfunction decodePath(raw: string): string {\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n}\n\n/**\n * Re-express every relative link in `md` from a new base directory — the merge's\n * half of the link convention.\n *\n * THE DEFECT THIS CLOSES is structural and predates the glossary. A feature's\n * `specs/<svc>/spec.md` sits four directories below the docs root; the living\n * `services/<svc>/spec.md` it merges into sits two. `loam archive` copies a\n * requirement's text verbatim, so `[Order](../../../../glossary/order.md)` —\n * correct where it was written, and clickable in the pull request that reviewed\n * it — lands in the living document pointing two directories above the\n * repository. The same is true of a delta requirement citing an ADR, which has\n * been possible since before this module existed.\n *\n * REWRITING IS THE ONLY ANSWER THAT KEEPS BOTH PROPERTIES. The alternative\n * considered was to have authors write the link relative to where the text will\n * END UP, leaving loam to touch nothing — but that link does not resolve in the\n * delta, and \"it renders as a link in pull-request review, which is where these\n * documents are actually read\" is the convention's own first argument. So the\n * author writes what is true where they write it, and the merge makes it true\n * where it lands. The target is unchanged: only the route to it is.\n *\n * It is a positional rewrite, never a re-render: the matched target's own span\n * is replaced and every other byte of the line — the link text, the title, the\n * prose around it — is left exactly as written.\n */\nexport function rebaseLinks(md: string, move: { from: string; to: string }): string {\n const from = resolve(move.from);\n const to = resolve(move.to);\n if (from === to) return md;\n const fenced = fenceTracker();\n // `split` with a capturing group keeps the separators, so a document's own\n // mixture of CRLF and LF survives a rewrite that touches neither.\n const parts = md.split(/(\\r?\\n)/);\n for (let i = 0; i < parts.length; i += 2) {\n const rawLine = parts[i]!;\n if (fenced(rawLine)) continue;\n parts[i] = rebaseLine(rawLine, from, to);\n }\n return parts.join(\"\");\n}\n\n/** One line's links re-expressed, right to left so earlier spans keep their offsets. */\nfunction rebaseLine(rawLine: string, from: string, to: string): string {\n const blanked = rawLine.replace(CODE_SPAN_RE, (m) => \" \".repeat(m.length));\n const spans: Array<[number, number, string]> = [];\n const collect = (m: RegExpExecArray): void => {\n const at = m.indices?.[2];\n if (at === undefined) return;\n const rebased = rebaseTarget(m[2]!, from, to);\n if (rebased !== null) spans.push([at[0], at[1], rebased]);\n };\n const definition = DEFINITION_RE.exec(blanked);\n if (definition) collect(definition);\n else {\n INLINE_RE.lastIndex = 0;\n for (let m = INLINE_RE.exec(blanked); m !== null; m = INLINE_RE.exec(blanked)) collect(m);\n }\n let out = rawLine;\n for (const [start, end, replacement] of spans.reverse()) {\n out = out.slice(0, start) + replacement + out.slice(end);\n }\n return out;\n}\n\n/**\n * One target, re-expressed from `to` instead of `from`, or null when there is\n * nothing to re-express.\n *\n * The exclusions are `push`'s exactly — an absolute URL, a site-root path and a\n * bare fragment mean the same thing from every directory — and the ANGLE-BRACKET\n * and PERCENT-ESCAPE forms are preserved rather than normalised: a rewrite that\n * silently changed `<a b.md>` into `a%20b.md` would be editing the author's\n * spelling under cover of moving their file.\n */\nfunction rebaseTarget(target: string, from: string, to: string): string | null {\n const angled = target.startsWith(\"<\") && target.endsWith(\">\");\n const raw = angled ? target.slice(1, -1) : target;\n if (raw === \"\" || raw.startsWith(\"#\") || raw.startsWith(\"/\")) return null;\n if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(raw)) return null;\n const hash = raw.indexOf(\"#\");\n const pathPart = hash === -1 ? raw : raw.slice(0, hash);\n const fragment = hash === -1 ? \"\" : raw.slice(hash);\n if (pathPart === \"\") return null;\n const encoded = pathPart.includes(\"%\");\n let rebased = relative(to, resolve(from, decodePath(pathPart))).split(sep).join(\"/\");\n // `relative` returns a bare name for a sibling, which markdown reads as a\n // path relative to the file anyway — but `./` is what an author writes and\n // what keeps the link distinguishable from a reference label.\n if (rebased !== \"\" && !rebased.startsWith(\".\")) rebased = `./${rebased}`;\n if (rebased === \"\") return null;\n if (!angled && (encoded || rebased.includes(\" \"))) rebased = rebased.split(\" \").join(\"%20\");\n const rewritten = rebased + fragment;\n return angled ? `<${rewritten}>` : rewritten;\n}\n"]}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * A string value that argv would read as a flag is refused before any command
3
+ * sees it: {"target": "--type"} must not become a parsed option. Commander
4
+ * cannot make this distinction after the fact — by then the token IS a flag —
5
+ * so the boundary is here, on every string that crosses, array members
6
+ * included. No shell is ever involved, so this is the only smuggling path.
7
+ */
8
+ function smuggled(property, value) {
9
+ return value.startsWith("-")
10
+ ? `argument '${property}' value '${value}' begins with '-' — a value argv would read as a flag is refused`
11
+ : null;
12
+ }
13
+ /** One string value checked for type and smuggling. */
14
+ function checkedString(property, value) {
15
+ if (typeof value !== "string")
16
+ return { ok: false, problem: `argument '${property}' must be a string` };
17
+ const bad = smuggled(property, value);
18
+ return bad === null ? { ok: true, tokens: [value] } : { ok: false, problem: bad };
19
+ }
20
+ /** An array value checked member by member — rebuilt, not cast, so the claim is earned. */
21
+ function checkedStrings(property, value) {
22
+ if (!Array.isArray(value))
23
+ return { ok: false, problem: `argument '${property}' must be an array of strings` };
24
+ const tokens = [];
25
+ for (const entry of value) {
26
+ const checked = checkedString(property, entry);
27
+ if (!checked.ok)
28
+ return checked;
29
+ tokens.push(...checked.tokens);
30
+ }
31
+ return { ok: true, tokens };
32
+ }
33
+ function positionalTokens(tool, args) {
34
+ const tokens = [];
35
+ for (const positional of tool.positionals) {
36
+ const value = args[positional.property];
37
+ if (value === undefined) {
38
+ if (positional.required)
39
+ return { ok: false, problem: `required argument '${positional.property}' is missing` };
40
+ continue;
41
+ }
42
+ const checked = positional.variadic
43
+ ? checkedStrings(positional.property, value)
44
+ : checkedString(positional.property, value);
45
+ if (!checked.ok)
46
+ return checked;
47
+ tokens.push(...checked.tokens);
48
+ }
49
+ return { ok: true, tokens };
50
+ }
51
+ function flagTokens(tool, args) {
52
+ const tokens = [];
53
+ for (const flag of tool.flags) {
54
+ const value = args[flag.property];
55
+ if (value === undefined) {
56
+ // The same refusal absent required positionals get: the client learns
57
+ // "-32602 missing argument", not an invalid-option envelope after the
58
+ // command already refused.
59
+ if (flag.required === true)
60
+ return { ok: false, problem: `required argument '${flag.property}' is missing` };
61
+ continue;
62
+ }
63
+ if (flag.kind === "boolean") {
64
+ if (typeof value !== "boolean")
65
+ return { ok: false, problem: `argument '${flag.property}' must be a boolean` };
66
+ if (value)
67
+ tokens.push(flag.flag);
68
+ continue;
69
+ }
70
+ const checked = flag.kind === "strings"
71
+ ? checkedStrings(flag.property, value)
72
+ : checkedString(flag.property, value);
73
+ if (!checked.ok)
74
+ return checked;
75
+ // A repeatable flag repeats per value; a single-valued one appears once.
76
+ for (const token of checked.tokens)
77
+ tokens.push(flag.flag, token);
78
+ }
79
+ return { ok: true, tokens };
80
+ }
81
+ /**
82
+ * The argv for one tool call — or the invalid-params problem that stops it.
83
+ * `--json` is always appended: the envelope is the tool-result contract, and
84
+ * a client cannot opt out of it into the human rendering. Unknown argument
85
+ * names refuse rather than vanish — the schema says additionalProperties
86
+ * false, and a server that enforced that only in the schema would silently
87
+ * drop the typo'd argument of any client that skipped validation.
88
+ */
89
+ export function toArgv(tool, args) {
90
+ const known = new Set([
91
+ ...tool.positionals.map((p) => p.property),
92
+ ...tool.flags.map((f) => f.property),
93
+ ]);
94
+ for (const property of Object.keys(args)) {
95
+ if (!known.has(property)) {
96
+ const allowed = known.size === 0 ? "no arguments" : [...known].join(", ");
97
+ return { ok: false, problem: `unknown argument '${property}' for ${tool.name} — expected ${allowed}` };
98
+ }
99
+ }
100
+ const positionals = positionalTokens(tool, args);
101
+ if (!positionals.ok)
102
+ return { ok: false, problem: positionals.problem };
103
+ const flags = flagTokens(tool, args);
104
+ if (!flags.ok)
105
+ return { ok: false, problem: flags.problem };
106
+ return {
107
+ ok: true,
108
+ argv: [tool.command, ...positionals.tokens, ...flags.tokens, ...(tool.fixed ?? []), "--json"],
109
+ };
110
+ }
111
+ //# sourceMappingURL=argv.js.map