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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1026) hide show
  1. package/CHANGELOG.md +2548 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +494 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +317 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +161 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +251 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +194 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +236 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +250 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +219 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +334 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +47 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +282 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +88 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  398. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  399. package/dist/core/c4/parsed/specification.js +78 -0
  400. package/dist/core/c4/parsed/specification.js.map +1 -0
  401. package/dist/core/c4/parsed/values.js +57 -0
  402. package/dist/core/c4/parsed/values.js.map +1 -0
  403. package/dist/core/c4/parsed/view-ids.js +75 -0
  404. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  405. package/dist/core/c4/project/architecture.js +45 -0
  406. package/dist/core/c4/project/architecture.js.map +1 -0
  407. package/dist/core/c4/project/documents.js +58 -0
  408. package/dist/core/c4/project/documents.js.map +1 -0
  409. package/dist/core/c4/project/load.js +183 -0
  410. package/dist/core/c4/project/load.js.map +1 -0
  411. package/dist/core/c4/resolve/service.js +113 -0
  412. package/dist/core/c4/resolve/service.js.map +1 -0
  413. package/dist/core/c4/seed/fleet-file.js +201 -0
  414. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  415. package/dist/core/c4/seed/items.js +91 -0
  416. package/dist/core/c4/seed/items.js.map +1 -0
  417. package/dist/core/c4/seed/stamp.js +48 -0
  418. package/dist/core/c4/seed/stamp.js.map +1 -0
  419. package/dist/core/c4/seed/template.js +109 -0
  420. package/dist/core/c4/seed/template.js.map +1 -0
  421. package/dist/core/c4/source-mask.js +82 -0
  422. package/dist/core/c4/source-mask.js.map +1 -0
  423. package/dist/core/c4/source-scan.js +227 -0
  424. package/dist/core/c4/source-scan.js.map +1 -0
  425. package/dist/core/c4/splice/authored-source.js +101 -0
  426. package/dist/core/c4/splice/authored-source.js.map +1 -0
  427. package/dist/core/c4/splice/contract.js +10 -0
  428. package/dist/core/c4/splice/contract.js.map +1 -0
  429. package/dist/core/c4/splice/delta-blocks.js +126 -0
  430. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  431. package/dist/core/c4/splice/identity/edges.js +36 -0
  432. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  433. package/dist/core/c4/splice/landscape-merge.js +269 -0
  434. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  435. package/dist/core/c4/splice/placement.js +194 -0
  436. package/dist/core/c4/splice/placement.js.map +1 -0
  437. package/dist/core/c4/workspace.js +172 -0
  438. package/dist/core/c4/workspace.js.map +1 -0
  439. package/dist/core/capabilities/authoring/notes.js +141 -0
  440. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  441. package/dist/core/capabilities/capabilities.js +124 -0
  442. package/dist/core/capabilities/capabilities.js.map +1 -0
  443. package/dist/core/capabilities/delta/doc.js +50 -0
  444. package/dist/core/capabilities/delta/doc.js.map +1 -0
  445. package/dist/core/capabilities/delta/overlay.js +129 -0
  446. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  447. package/dist/core/capabilities/delta/summary.js +57 -0
  448. package/dist/core/capabilities/delta/summary.js.map +1 -0
  449. package/dist/core/capabilities/delta/tree.js +43 -0
  450. package/dist/core/capabilities/delta/tree.js.map +1 -0
  451. package/dist/core/capabilities/delta/uncovered.js +222 -0
  452. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  453. package/dist/core/capabilities/findings.js +342 -0
  454. package/dist/core/capabilities/findings.js.map +1 -0
  455. package/dist/core/capabilities/realizes/findings.js +181 -0
  456. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  457. package/dist/core/capabilities/realizes/join.js +143 -0
  458. package/dist/core/capabilities/realizes/join.js.map +1 -0
  459. package/dist/core/capabilities/rollup.js +199 -0
  460. package/dist/core/capabilities/rollup.js.map +1 -0
  461. package/dist/core/capabilities/tree.js +126 -0
  462. package/dist/core/capabilities/tree.js.map +1 -0
  463. package/dist/core/capabilities/usecase-join.js +277 -0
  464. package/dist/core/capabilities/usecase-join.js.map +1 -0
  465. package/dist/core/coherence/authoring/scaffold.js +177 -0
  466. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  467. package/dist/core/coherence/authoring/sentinels.js +74 -0
  468. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  469. package/dist/core/coherence/coherence.js +251 -0
  470. package/dist/core/coherence/coherence.js.map +1 -0
  471. package/dist/core/coherence/declared.js +269 -0
  472. package/dist/core/coherence/declared.js.map +1 -0
  473. package/dist/core/coherence/events/declared.js +206 -0
  474. package/dist/core/coherence/events/declared.js.map +1 -0
  475. package/dist/core/coherence/events/events.js +204 -0
  476. package/dist/core/coherence/events/events.js.map +1 -0
  477. package/dist/core/coherence/events/grades.js +166 -0
  478. package/dist/core/coherence/events/grades.js.map +1 -0
  479. package/dist/core/coherence/events/lookups.js +122 -0
  480. package/dist/core/coherence/events/lookups.js.map +1 -0
  481. package/dist/core/coherence/events/merged.js +40 -0
  482. package/dist/core/coherence/events/merged.js.map +1 -0
  483. package/dist/core/coherence/living.js +198 -0
  484. package/dist/core/coherence/living.js.map +1 -0
  485. package/dist/core/coherence/lookups.js +155 -0
  486. package/dist/core/coherence/lookups.js.map +1 -0
  487. package/dist/core/coherence/pending.js +39 -0
  488. package/dist/core/coherence/pending.js.map +1 -0
  489. package/dist/core/conflict-markers.js +60 -0
  490. package/dist/core/conflict-markers.js.map +1 -0
  491. package/dist/core/delta/claims.js +73 -0
  492. package/dist/core/delta/claims.js.map +1 -0
  493. package/dist/core/delta/delta.js +296 -0
  494. package/dist/core/delta/delta.js.map +1 -0
  495. package/dist/core/delta/document.js +177 -0
  496. package/dist/core/delta/document.js.map +1 -0
  497. package/dist/core/delta/scope.js +48 -0
  498. package/dist/core/delta/scope.js.map +1 -0
  499. package/dist/core/delta/select.js +237 -0
  500. package/dist/core/delta/select.js.map +1 -0
  501. package/dist/core/dependencies/dependencies.js +141 -0
  502. package/dist/core/dependencies/dependencies.js.map +1 -0
  503. package/dist/core/dependencies/facts.js +165 -0
  504. package/dist/core/dependencies/facts.js.map +1 -0
  505. package/dist/core/dependencies/fanin.js +88 -0
  506. package/dist/core/dependencies/fanin.js.map +1 -0
  507. package/dist/core/dependencies/graph.js +106 -0
  508. package/dist/core/dependencies/graph.js.map +1 -0
  509. package/dist/core/diff/base-git.js +148 -0
  510. package/dist/core/diff/base-git.js.map +1 -0
  511. package/dist/core/diff/base-state.js +209 -0
  512. package/dist/core/diff/base-state.js.map +1 -0
  513. package/dist/core/diff/current-state.js +96 -0
  514. package/dist/core/diff/current-state.js.map +1 -0
  515. package/dist/core/diff/scope/changed-paths.js +188 -0
  516. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  517. package/dist/core/diff/semantic.js +243 -0
  518. package/dist/core/diff/semantic.js.map +1 -0
  519. package/dist/core/diff/victims.js +247 -0
  520. package/dist/core/diff/victims.js.map +1 -0
  521. package/dist/core/docs.js +7 -64
  522. package/dist/core/docs.js.map +1 -1
  523. package/dist/core/doctor/agents.js +222 -0
  524. package/dist/core/doctor/agents.js.map +1 -0
  525. package/dist/core/doctor/config.js +135 -0
  526. package/dist/core/doctor/config.js.map +1 -0
  527. package/dist/core/doctor/doctor.js +275 -0
  528. package/dist/core/doctor/doctor.js.map +1 -0
  529. package/dist/core/doctor/report.js +13 -0
  530. package/dist/core/doctor/report.js.map +1 -0
  531. package/dist/core/doctor/residue.js +110 -0
  532. package/dist/core/doctor/residue.js.map +1 -0
  533. package/dist/core/document/apply.js +77 -0
  534. package/dist/core/document/apply.js.map +1 -0
  535. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  536. package/dist/core/document/frontmatter.js.map +1 -0
  537. package/dist/core/document/grammar/directives.js +143 -0
  538. package/dist/core/document/grammar/directives.js.map +1 -0
  539. package/dist/core/document/parse.js +263 -0
  540. package/dist/core/document/parse.js.map +1 -0
  541. package/dist/core/document/scenarios.js +221 -0
  542. package/dist/core/document/scenarios.js.map +1 -0
  543. package/dist/core/document/spec.js +241 -0
  544. package/dist/core/document/spec.js.map +1 -0
  545. package/dist/core/{config.js → envelope/config.js} +79 -18
  546. package/dist/core/envelope/config.js.map +1 -0
  547. package/dist/core/envelope/json.js +175 -0
  548. package/dist/core/envelope/json.js.map +1 -0
  549. package/dist/core/{version.js → envelope/version.js} +3 -3
  550. package/dist/core/envelope/version.js.map +1 -0
  551. package/dist/core/explain/families.js +175 -0
  552. package/dist/core/explain/families.js.map +1 -0
  553. package/dist/core/explain/fix-tables.js +178 -0
  554. package/dist/core/explain/fix-tables.js.map +1 -0
  555. package/dist/core/explain/lookup.js +181 -0
  556. package/dist/core/explain/lookup.js.map +1 -0
  557. package/dist/core/explain/refusals.js +49 -0
  558. package/dist/core/explain/refusals.js.map +1 -0
  559. package/dist/core/explain/terms.js +144 -0
  560. package/dist/core/explain/terms.js.map +1 -0
  561. package/dist/core/explore/describe.js +105 -0
  562. package/dist/core/explore/describe.js.map +1 -0
  563. package/dist/core/{explore.js → explore/explore.js} +50 -101
  564. package/dist/core/explore/explore.js.map +1 -0
  565. package/dist/core/fleet-context.js +120 -78
  566. package/dist/core/fleet-context.js.map +1 -1
  567. package/dist/core/gate/checks.js +201 -0
  568. package/dist/core/gate/checks.js.map +1 -0
  569. package/dist/core/gate/partners.js +205 -0
  570. package/dist/core/gate/partners.js.map +1 -0
  571. package/dist/core/gate/report.js +47 -0
  572. package/dist/core/gate/report.js.map +1 -0
  573. package/dist/core/gate/verification.js +113 -0
  574. package/dist/core/gate/verification.js.map +1 -0
  575. package/dist/core/gherkin/digest.js +60 -0
  576. package/dist/core/gherkin/digest.js.map +1 -0
  577. package/dist/core/gherkin/emit.js +177 -0
  578. package/dist/core/gherkin/emit.js.map +1 -0
  579. package/dist/core/gherkin/read.js +76 -0
  580. package/dist/core/gherkin/read.js.map +1 -0
  581. package/dist/core/gherkin/stale.js +234 -0
  582. package/dist/core/gherkin/stale.js.map +1 -0
  583. package/dist/core/gherkin/stamp.js +77 -0
  584. package/dist/core/gherkin/stamp.js.map +1 -0
  585. package/dist/core/gherkin/steps/catalogue.js +154 -0
  586. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  587. package/dist/core/gherkin/steps/inventory.js +98 -0
  588. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  589. package/dist/core/gherkin/steps/phrase.js +72 -0
  590. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  591. package/dist/core/glossary/delta.js +106 -0
  592. package/dist/core/glossary/delta.js.map +1 -0
  593. package/dist/core/glossary/tree.js +86 -0
  594. package/dist/core/glossary/tree.js.map +1 -0
  595. package/dist/core/kernel/concurrency.js.map +1 -0
  596. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  597. package/dist/core/kernel/document-bytes.js.map +1 -0
  598. package/dist/core/kernel/fences.js +42 -0
  599. package/dist/core/kernel/fences.js.map +1 -0
  600. package/dist/core/kernel/ids/capability.js +72 -0
  601. package/dist/core/kernel/ids/capability.js.map +1 -0
  602. package/dist/core/kernel/ids/dirs.js +33 -0
  603. package/dist/core/kernel/ids/dirs.js.map +1 -0
  604. package/dist/core/kernel/ids/feature.js +75 -0
  605. package/dist/core/kernel/ids/feature.js.map +1 -0
  606. package/dist/core/kernel/ids/service.js +156 -0
  607. package/dist/core/kernel/ids/service.js.map +1 -0
  608. package/dist/core/kernel/ids/subsystem.js +63 -0
  609. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  610. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  611. package/dist/core/kernel/path-safety.js.map +1 -0
  612. package/dist/core/kernel/records.js.map +1 -0
  613. package/dist/core/links/backlinks.js +68 -0
  614. package/dist/core/links/backlinks.js.map +1 -0
  615. package/dist/core/links/case.js +74 -0
  616. package/dist/core/links/case.js.map +1 -0
  617. package/dist/core/links/corpus.js +104 -0
  618. package/dist/core/links/corpus.js.map +1 -0
  619. package/dist/core/links/findings.js +110 -0
  620. package/dist/core/links/findings.js.map +1 -0
  621. package/dist/core/links/parse.js +236 -0
  622. package/dist/core/links/parse.js.map +1 -0
  623. package/dist/core/mcp/argv.js +111 -0
  624. package/dist/core/mcp/argv.js.map +1 -0
  625. package/dist/core/mcp/author-tools.js +72 -0
  626. package/dist/core/mcp/author-tools.js.map +1 -0
  627. package/dist/core/mcp/framing.js +46 -0
  628. package/dist/core/mcp/framing.js.map +1 -0
  629. package/dist/core/mcp/protocol.js +280 -0
  630. package/dist/core/mcp/protocol.js.map +1 -0
  631. package/dist/core/mcp/tools.js +314 -0
  632. package/dist/core/mcp/tools.js.map +1 -0
  633. package/dist/core/obligations/obligations.js +106 -0
  634. package/dist/core/obligations/obligations.js.map +1 -0
  635. package/dist/core/openapi/baseline/gate.js +166 -0
  636. package/dist/core/openapi/baseline/gate.js.map +1 -0
  637. package/dist/core/openapi/baseline/plan.js +112 -0
  638. package/dist/core/openapi/baseline/plan.js.map +1 -0
  639. package/dist/core/openapi/baseline/record.js +175 -0
  640. package/dist/core/openapi/baseline/record.js.map +1 -0
  641. package/dist/core/openapi/depth.js +122 -0
  642. package/dist/core/openapi/depth.js.map +1 -0
  643. package/dist/core/openapi/digest.js +189 -0
  644. package/dist/core/openapi/digest.js.map +1 -0
  645. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  646. package/dist/core/openapi/doc.js.map +1 -0
  647. package/dist/core/openapi/merge/components.js +234 -0
  648. package/dist/core/openapi/merge/components.js.map +1 -0
  649. package/dist/core/openapi/merge/error.js +26 -0
  650. package/dist/core/openapi/merge/error.js.map +1 -0
  651. package/dist/core/openapi/merge/markers.js +208 -0
  652. package/dist/core/openapi/merge/markers.js.map +1 -0
  653. package/dist/core/openapi/merge/merge.js +256 -0
  654. package/dist/core/openapi/merge/merge.js.map +1 -0
  655. package/dist/core/openapi/merge/pin.js +128 -0
  656. package/dist/core/openapi/merge/pin.js.map +1 -0
  657. package/dist/core/openspec/decide/capabilities.js +244 -0
  658. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  659. package/dist/core/openspec/decide/dispositions.js +63 -0
  660. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  661. package/dist/core/openspec/decide/renames.js +179 -0
  662. package/dist/core/openspec/decide/renames.js.map +1 -0
  663. package/dist/core/openspec/decide/verdict.js +84 -0
  664. package/dist/core/openspec/decide/verdict.js.map +1 -0
  665. package/dist/core/openspec/inventory.js +221 -0
  666. package/dist/core/openspec/inventory.js.map +1 -0
  667. package/dist/core/openspec/model/mapping.js +76 -0
  668. package/dist/core/openspec/model/mapping.js.map +1 -0
  669. package/dist/core/openspec/model/model.js +65 -0
  670. package/dist/core/openspec/model/model.js.map +1 -0
  671. package/dist/core/openspec/scan/changes.js +235 -0
  672. package/dist/core/openspec/scan/changes.js.map +1 -0
  673. package/dist/core/openspec/scan/config.js +200 -0
  674. package/dist/core/openspec/scan/config.js.map +1 -0
  675. package/dist/core/openspec/scan/shape.js +260 -0
  676. package/dist/core/openspec/scan/shape.js.map +1 -0
  677. package/dist/core/openspec/scan/walk.js +133 -0
  678. package/dist/core/openspec/scan/walk.js.map +1 -0
  679. package/dist/core/openspec/scan/workspace.js +71 -0
  680. package/dist/core/openspec/scan/workspace.js.map +1 -0
  681. package/dist/core/owners/codeowners.js +141 -0
  682. package/dist/core/owners/codeowners.js.map +1 -0
  683. package/dist/core/pack/features.js +120 -0
  684. package/dist/core/pack/features.js.map +1 -0
  685. package/dist/core/pack/joins.js +112 -0
  686. package/dist/core/pack/joins.js.map +1 -0
  687. package/dist/core/pack/living.js +118 -0
  688. package/dist/core/pack/living.js.map +1 -0
  689. package/dist/core/pack/pack.js +76 -0
  690. package/dist/core/pack/pack.js.map +1 -0
  691. package/dist/core/permissions/permissions.js +87 -0
  692. package/dist/core/permissions/permissions.js.map +1 -0
  693. package/dist/core/projection/api.js +84 -0
  694. package/dist/core/projection/api.js.map +1 -0
  695. package/dist/core/projection/arch-slice.js +63 -0
  696. package/dist/core/projection/arch-slice.js.map +1 -0
  697. package/dist/core/projection/events.js +34 -0
  698. package/dist/core/projection/events.js.map +1 -0
  699. package/dist/core/provenance/findings.js +208 -0
  700. package/dist/core/provenance/findings.js.map +1 -0
  701. package/dist/core/provenance/git.js +225 -0
  702. package/dist/core/provenance/git.js.map +1 -0
  703. package/dist/core/provenance/gitq/moves.js +182 -0
  704. package/dist/core/provenance/gitq/moves.js.map +1 -0
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  707. package/dist/core/provenance/sample/scope.js +238 -0
  708. package/dist/core/provenance/sample/scope.js.map +1 -0
  709. package/dist/core/provenance/sample/sections.js +61 -0
  710. package/dist/core/provenance/sample/sections.js.map +1 -0
  711. package/dist/core/provenance/sources.js +263 -0
  712. package/dist/core/provenance/sources.js.map +1 -0
  713. package/dist/core/provenance/stamp.js +232 -0
  714. package/dist/core/provenance/stamp.js.map +1 -0
  715. package/dist/core/provenance/walk.js +157 -0
  716. package/dist/core/provenance/walk.js.map +1 -0
  717. package/dist/core/repo/authored/paths.js +192 -0
  718. package/dist/core/repo/authored/paths.js.map +1 -0
  719. package/dist/core/repo/entries.js +126 -0
  720. package/dist/core/repo/entries.js.map +1 -0
  721. package/dist/core/repo/paths.js +236 -0
  722. package/dist/core/repo/paths.js.map +1 -0
  723. package/dist/core/repo/repo.js +248 -0
  724. package/dist/core/repo/repo.js.map +1 -0
  725. package/dist/core/repo/service-target.js +153 -0
  726. package/dist/core/repo/service-target.js.map +1 -0
  727. package/dist/core/repo/state.js +58 -0
  728. package/dist/core/repo/state.js.map +1 -0
  729. package/dist/core/repo/tree/find.js +52 -0
  730. package/dist/core/repo/tree/find.js.map +1 -0
  731. package/dist/core/repo/tree/fs.js +64 -0
  732. package/dist/core/repo/tree/fs.js.map +1 -0
  733. package/dist/core/repo/tree/marker.js +61 -0
  734. package/dist/core/repo/tree/marker.js.map +1 -0
  735. package/dist/core/repo/tree/views.js +172 -0
  736. package/dist/core/repo/tree/views.js.map +1 -0
  737. package/dist/core/repo/tree/walk.js +234 -0
  738. package/dist/core/repo/tree/walk.js.map +1 -0
  739. package/dist/core/results.js +99 -2
  740. package/dist/core/results.js.map +1 -1
  741. package/dist/core/scaffold/landscape.js +131 -0
  742. package/dist/core/scaffold/landscape.js.map +1 -0
  743. package/dist/core/scaffold/readme.js +84 -0
  744. package/dist/core/scaffold/readme.js.map +1 -0
  745. package/dist/core/staging/commit.js +241 -0
  746. package/dist/core/staging/commit.js.map +1 -0
  747. package/dist/core/staging/interrupted.js +96 -0
  748. package/dist/core/staging/interrupted.js.map +1 -0
  749. package/dist/core/staging/lock.js +206 -0
  750. package/dist/core/staging/lock.js.map +1 -0
  751. package/dist/core/staging/recovery/finding.js +35 -0
  752. package/dist/core/staging/recovery/finding.js.map +1 -0
  753. package/dist/core/staging/recovery/intent.js +98 -0
  754. package/dist/core/staging/recovery/intent.js.map +1 -0
  755. package/dist/core/staging/recovery/recover.js +126 -0
  756. package/dist/core/staging/recovery/recover.js.map +1 -0
  757. package/dist/core/staging/recovery/residue.js +62 -0
  758. package/dist/core/staging/recovery/residue.js.map +1 -0
  759. package/dist/core/staging/snapshot.js +202 -0
  760. package/dist/core/staging/snapshot.js.map +1 -0
  761. package/dist/core/staging/txn/forward.js +171 -0
  762. package/dist/core/staging/txn/forward.js.map +1 -0
  763. package/dist/core/staging/txn/journal.js +139 -0
  764. package/dist/core/staging/txn/journal.js.map +1 -0
  765. package/dist/core/staging/txn/transaction.js +75 -0
  766. package/dist/core/staging/txn/transaction.js.map +1 -0
  767. package/dist/core/staging/writes.js +113 -0
  768. package/dist/core/staging/writes.js.map +1 -0
  769. package/dist/core/status/actions/execution.js +49 -0
  770. package/dist/core/status/actions/execution.js.map +1 -0
  771. package/dist/core/status/contracts.js +102 -0
  772. package/dist/core/status/contracts.js.map +1 -0
  773. package/dist/core/status/feature/artifacts.js +171 -0
  774. package/dist/core/status/feature/artifacts.js.map +1 -0
  775. package/dist/core/status/feature/feature.js +207 -0
  776. package/dist/core/status/feature/feature.js.map +1 -0
  777. package/dist/core/status/feature/next.js +150 -0
  778. package/dist/core/status/feature/next.js.map +1 -0
  779. package/dist/core/status/feature/state.js +21 -0
  780. package/dist/core/status/feature/state.js.map +1 -0
  781. package/dist/core/status/fleet/fleet.js +187 -0
  782. package/dist/core/status/fleet/fleet.js.map +1 -0
  783. package/dist/core/status/fleet/next.js +207 -0
  784. package/dist/core/status/fleet/next.js.map +1 -0
  785. package/dist/core/status/interrupted.js +97 -0
  786. package/dist/core/status/interrupted.js.map +1 -0
  787. package/dist/core/status/report.js +31 -0
  788. package/dist/core/status/report.js.map +1 -0
  789. package/dist/core/status/scan.js +41 -0
  790. package/dist/core/status/scan.js.map +1 -0
  791. package/dist/core/status/verification.js +201 -0
  792. package/dist/core/status/verification.js.map +1 -0
  793. package/dist/core/usecases/capability.js +169 -0
  794. package/dist/core/usecases/capability.js.map +1 -0
  795. package/dist/core/usecases/delta/claims.js +116 -0
  796. package/dist/core/usecases/delta/claims.js.map +1 -0
  797. package/dist/core/usecases/delta/flows.js +103 -0
  798. package/dist/core/usecases/delta/flows.js.map +1 -0
  799. package/dist/core/usecases/delta/overlay.js +235 -0
  800. package/dist/core/usecases/delta/overlay.js.map +1 -0
  801. package/dist/core/usecases/fleet.js +137 -0
  802. package/dist/core/usecases/fleet.js.map +1 -0
  803. package/dist/core/usecases/operations.js +111 -0
  804. package/dist/core/usecases/operations.js.map +1 -0
  805. package/dist/core/usecases/place.js +44 -0
  806. package/dist/core/usecases/place.js.map +1 -0
  807. package/dist/core/usecases/touch.js +101 -0
  808. package/dist/core/usecases/touch.js.map +1 -0
  809. package/dist/core/verify/answers.js +173 -0
  810. package/dist/core/verify/answers.js.map +1 -0
  811. package/dist/core/verify/build.js +150 -0
  812. package/dist/core/verify/build.js.map +1 -0
  813. package/dist/core/verify/checklist.js +201 -0
  814. package/dist/core/verify/checklist.js.map +1 -0
  815. package/dist/core/verify/claims/identity.js +61 -0
  816. package/dist/core/verify/claims/identity.js.map +1 -0
  817. package/dist/core/verify/cross/diff.js +97 -0
  818. package/dist/core/verify/cross/diff.js.map +1 -0
  819. package/dist/core/verify/evidence/contract.js +169 -0
  820. package/dist/core/verify/evidence/contract.js.map +1 -0
  821. package/dist/core/verify/file.js +257 -0
  822. package/dist/core/verify/file.js.map +1 -0
  823. package/dist/core/verify/pins/lint.js +157 -0
  824. package/dist/core/verify/pins/lint.js.map +1 -0
  825. package/dist/core/verify/pins/pin.js +95 -0
  826. package/dist/core/verify/pins/pin.js.map +1 -0
  827. package/dist/core/verify/record.js +142 -0
  828. package/dist/core/verify/record.js.map +1 -0
  829. package/dist/core/verify/store/commit.js +89 -0
  830. package/dist/core/verify/store/commit.js.map +1 -0
  831. package/dist/core/verify/store/render.js +57 -0
  832. package/dist/core/verify/store/render.js.map +1 -0
  833. package/dist/core/vocabulary/health.js +91 -0
  834. package/dist/core/vocabulary/health.js.map +1 -0
  835. package/dist/core/vocabulary/issue.js +55 -0
  836. package/dist/core/vocabulary/issue.js.map +1 -0
  837. package/dist/core/vocabulary/maturity.js +145 -0
  838. package/dist/core/vocabulary/maturity.js.map +1 -0
  839. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  840. package/dist/core/vocabulary/report.js.map +1 -0
  841. package/dist/core/vocabulary/steps.js +266 -0
  842. package/dist/core/vocabulary/steps.js.map +1 -0
  843. package/dist/core/workspace/discover.js +239 -0
  844. package/dist/core/workspace/discover.js.map +1 -0
  845. package/dist/core/workspace/render.js +52 -0
  846. package/dist/core/workspace/render.js.map +1 -0
  847. package/examples/README.md +189 -0
  848. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  849. package/examples/docs/architecture/capabilities.yaml +31 -0
  850. package/examples/docs/architecture/landscape.likec4 +280 -0
  851. package/examples/docs/architecture/obligations.yaml +23 -0
  852. package/examples/docs/architecture/permissions.yaml +50 -0
  853. package/examples/docs/architecture/subsystems.likec4 +11 -0
  854. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  855. package/examples/docs/capabilities/checkout/spec.md +55 -0
  856. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  857. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  858. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  859. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  860. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  863. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  864. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  865. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  866. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  867. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  868. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  869. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  870. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  871. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  872. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  873. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  874. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  875. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  876. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  877. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  878. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  879. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  882. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  883. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  884. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  885. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  886. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  887. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  888. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  889. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  890. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  891. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  896. package/examples/docs/glossary/order.md +15 -0
  897. package/examples/docs/glossary/payments/authorization.md +14 -0
  898. package/examples/docs/likec4.config.json +9 -0
  899. package/examples/docs/loam.json +3 -0
  900. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  901. package/examples/docs/services/checkout-web/spec.md +91 -0
  902. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  903. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  904. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  905. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  906. package/examples/docs/services/order-service/arch.spec.md +127 -0
  907. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  908. package/examples/docs/services/order-service/health.yaml +57 -0
  909. package/examples/docs/services/order-service/model.likec4 +138 -0
  910. package/examples/docs/services/order-service/openapi.yaml +257 -0
  911. package/examples/docs/services/order-service/runbook.md +62 -0
  912. package/examples/docs/services/order-service/spec.md +134 -0
  913. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  914. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  915. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  916. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  917. package/examples/docs/services/payment-service/health.yaml +39 -0
  918. package/examples/docs/services/payment-service/model.likec4 +67 -0
  919. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  920. package/examples/docs/services/payment-service/runbook.md +37 -0
  921. package/examples/docs/services/payment-service/spec.md +88 -0
  922. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  923. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  924. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  925. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  926. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  927. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  928. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  929. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  930. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  931. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  932. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  933. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  934. package/examples/docs/services/platform/subsystem.yaml +2 -0
  935. package/examples/fleet.yaml +57 -0
  936. package/package.json +19 -6
  937. package/dist/commands/adopt.js +0 -240
  938. package/dist/commands/adopt.js.map +0 -1
  939. package/dist/commands/archive.js +0 -1313
  940. package/dist/commands/archive.js.map +0 -1
  941. package/dist/commands/delta.js +0 -388
  942. package/dist/commands/delta.js.map +0 -1
  943. package/dist/commands/docs-repo-gate.js.map +0 -1
  944. package/dist/commands/format.js +0 -17
  945. package/dist/commands/format.js.map +0 -1
  946. package/dist/commands/gherkin.js +0 -309
  947. package/dist/commands/gherkin.js.map +0 -1
  948. package/dist/commands/init.js.map +0 -1
  949. package/dist/commands/list.js +0 -295
  950. package/dist/commands/list.js.map +0 -1
  951. package/dist/commands/migrate-openspec.js +0 -950
  952. package/dist/commands/migrate-openspec.js.map +0 -1
  953. package/dist/commands/new.js +0 -413
  954. package/dist/commands/new.js.map +0 -1
  955. package/dist/commands/rebase.js +0 -356
  956. package/dist/commands/rebase.js.map +0 -1
  957. package/dist/commands/show.js.map +0 -1
  958. package/dist/commands/status.js +0 -262
  959. package/dist/commands/status.js.map +0 -1
  960. package/dist/commands/unarchive.js.map +0 -1
  961. package/dist/commands/validate.js +0 -1773
  962. package/dist/commands/validate.js.map +0 -1
  963. package/dist/commands/verify.js +0 -775
  964. package/dist/commands/verify.js.map +0 -1
  965. package/dist/commands/vouch.js +0 -421
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2173
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js.map +0 -1
  970. package/dist/core/arch.js +0 -130
  971. package/dist/core/arch.js.map +0 -1
  972. package/dist/core/asyncapi.js.map +0 -1
  973. package/dist/core/brief.js +0 -755
  974. package/dist/core/brief.js.map +0 -1
  975. package/dist/core/coherence.js +0 -577
  976. package/dist/core/coherence.js.map +0 -1
  977. package/dist/core/concurrency.js.map +0 -1
  978. package/dist/core/config.js.map +0 -1
  979. package/dist/core/delta.js +0 -476
  980. package/dist/core/delta.js.map +0 -1
  981. package/dist/core/dependencies.js +0 -356
  982. package/dist/core/dependencies.js.map +0 -1
  983. package/dist/core/doctor.js +0 -596
  984. package/dist/core/doctor.js.map +0 -1
  985. package/dist/core/document-bytes.js.map +0 -1
  986. package/dist/core/explore.js.map +0 -1
  987. package/dist/core/frontmatter.js.map +0 -1
  988. package/dist/core/gherkin.js +0 -463
  989. package/dist/core/gherkin.js.map +0 -1
  990. package/dist/core/health.js +0 -67
  991. package/dist/core/health.js.map +0 -1
  992. package/dist/core/ids.js +0 -122
  993. package/dist/core/ids.js.map +0 -1
  994. package/dist/core/issue.js +0 -9
  995. package/dist/core/issue.js.map +0 -1
  996. package/dist/core/json.js +0 -74
  997. package/dist/core/json.js.map +0 -1
  998. package/dist/core/likec4.js +0 -489
  999. package/dist/core/likec4.js.map +0 -1
  1000. package/dist/core/maturity.js +0 -66
  1001. package/dist/core/maturity.js.map +0 -1
  1002. package/dist/core/openapi-merge.js +0 -552
  1003. package/dist/core/openapi-merge.js.map +0 -1
  1004. package/dist/core/openapi.js.map +0 -1
  1005. package/dist/core/openspec-inventory.js +0 -1440
  1006. package/dist/core/openspec-inventory.js.map +0 -1
  1007. package/dist/core/path-safety.js.map +0 -1
  1008. package/dist/core/provenance.js +0 -787
  1009. package/dist/core/provenance.js.map +0 -1
  1010. package/dist/core/records.js.map +0 -1
  1011. package/dist/core/repo.js +0 -438
  1012. package/dist/core/repo.js.map +0 -1
  1013. package/dist/core/report.js.map +0 -1
  1014. package/dist/core/spec.js +0 -505
  1015. package/dist/core/spec.js.map +0 -1
  1016. package/dist/core/staging.js +0 -890
  1017. package/dist/core/staging.js.map +0 -1
  1018. package/dist/core/status.js +0 -1132
  1019. package/dist/core/status.js.map +0 -1
  1020. package/dist/core/steps.js +0 -72
  1021. package/dist/core/steps.js.map +0 -1
  1022. package/dist/core/verify.js +0 -807
  1023. package/dist/core/verify.js.map +0 -1
  1024. package/dist/core/version.js.map +0 -1
  1025. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1026. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,209 @@
1
+ /**
2
+ * The base side of `loam diff`: the fleet as the base commit held it, read
3
+ * entirely out of git's object store (`./base-git.ts`) and parsed with the
4
+ * SAME parsers the living files go through — `parseRequirements`,
5
+ * `openapiFromText`, `asyncapiFromText` — so a base document can never read
6
+ * differently from the identical bytes sitting on disk.
7
+ *
8
+ * CLASSIFICATION is a second, simplified spelling of `repo/tree/walk.ts`'s
9
+ * rule, and that is a recorded risk, not an accident: the walk classifies a
10
+ * LIVE filesystem (readdir, symlinks, marker files) and cannot walk a git
11
+ * ref. What keeps the two honest is sharing the artifact-name table
12
+ * (`ARTIFACT_FILES`/`isServiceArtifactName` from `repo/paths.ts`) and the
13
+ * marker name (`SUBSYSTEM_MARKER`) instead of respelling them, and restating
14
+ * — exactly — the walk's SERVICE branches: (1) a directory holding a service
15
+ * artifact is a service, marker present or not (artifacts win); (2) the leaf
16
+ * rule — neither marker nor artifact and nothing beneath (an empty or
17
+ * not-yet-adopted directory, a lone README.md, a lone `.gitkeep` — which is
18
+ * the whole content of a directory `loam seed` creates) is a service too; (3) nothing
19
+ * inside a PLAIN service is classified again (`examples/openapi.yaml` two
20
+ * levels down is interior, the walk stops there), but a service carrying the
21
+ * marker BESIDE its artifacts is still descended — minus artifact-named
22
+ * children like `adrs/` — so the services stranded beneath it stay
23
+ * enumerated, the walk's marker-misplaced branch. Subsystem and unmarked
24
+ * group directories need no restating: a flat file listing descends through
25
+ * them by construction. What is deliberately NOT restated: the walk's
26
+ * findings (`subsystem.marker-misplaced`, `subsystem.unmarked`,
27
+ * `subsystem.name-collision`) — those are validate's live diagnostics; here
28
+ * a name claimed twice suspends the subject (`ambiguous`) instead. If
29
+ * walk.ts's rule ever changes, this module owes the same change;
30
+ * test/diff.test.ts pins the visible agreement (subsystem move, not-adopted
31
+ * leaf, stranded-under-marker).
32
+ *
33
+ * Deliberate consequence of joining by leaf directory name: a service MOVED
34
+ * between subsystems keeps its id and diffs as UNCHANGED. Subsystem placement
35
+ * is navigation, not identity — SCHEMA's own position, and the reason
36
+ * `subsystem move` never touches a join key.
37
+ */
38
+ import { isUtf8 } from "node:buffer";
39
+ import { inOrder } from "../kernel/concurrency.js";
40
+ import { parseRequirements } from "../document/parse.js";
41
+ import { openapiFromText } from "../openapi/doc.js";
42
+ import { asyncapiFromText } from "../asyncapi/read.js";
43
+ import { ARTIFACT_FILES, isServiceArtifactName } from "../repo/paths.js";
44
+ import { SUBSYSTEM_MARKER } from "../repo/tree/marker.js";
45
+ import { listBaseTree, showBaseFile } from "./base-git.js";
46
+ /**
47
+ * Classify the base tree's file listing into service directories — the module
48
+ * banner's three restated walk branches, in depth order so every ancestor is
49
+ * decided before anything beneath it.
50
+ */
51
+ function classifyBaseDirs(paths) {
52
+ const fileChildren = new Map();
53
+ const dirChildren = new Map();
54
+ // Directories nothing VISIBLE lives in, so neither map above names them.
55
+ const bare = new Set();
56
+ for (const path of paths) {
57
+ const all = path.split("/");
58
+ // Dot-named entries are invisible to the live walk — but the walk still
59
+ // SEES the directory holding them, because readdir discovered the
60
+ // directory itself and only its CONTENTS are filtered (walk.ts's
61
+ // `subdirNames` and `visitDir`). So cut the path at its first dot-named
62
+ // segment rather than discarding the whole path: everything above the cut
63
+ // stays a visible directory, the cut segment and everything beneath it
64
+ // stay invisible. Discarding it outright hid every directory whose only
65
+ // file is dot-named — which is exactly what `loam seed` writes into a
66
+ // service directory it creates (`.gitkeep`, and nothing else), so a seeded
67
+ // service was absent from the base and diffed as `diff.service-added`
68
+ // against the unchanged tree that had just been committed.
69
+ const cut = all.findIndex((s) => s.startsWith(".") || s === "");
70
+ const segments = cut === -1 ? all : all.slice(0, cut);
71
+ // Only a whole path ends in a file; a cut one is a pure directory chain.
72
+ const fileAt = cut === -1 ? segments.length - 1 : -1;
73
+ for (let i = 1; i < segments.length; i += 1) {
74
+ const dir = segments.slice(0, i).join("/");
75
+ const child = segments[i];
76
+ const into = i === fileAt ? fileChildren : dirChildren;
77
+ let set = into.get(dir);
78
+ if (set === undefined)
79
+ into.set(dir, (set = new Set()));
80
+ set.add(child);
81
+ }
82
+ if (fileAt === -1 && segments.length > 1)
83
+ bare.add(segments.join("/"));
84
+ }
85
+ const hasMarker = (dir) => fileChildren.get(dir)?.has(SUBSYSTEM_MARKER) === true;
86
+ const hasArtifact = (dir) => [...(fileChildren.get(dir) ?? [])].some((f) => isServiceArtifactName(f, "file")) ||
87
+ [...(dirChildren.get(dir) ?? [])].some((d) => isServiceArtifactName(d, "dir"));
88
+ // The walk's leaf rule: neither marker nor artifact and nothing beneath is a
89
+ // service. (A truly EMPTY directory is unrepresentable in a git tree, so at
90
+ // the base this arm means "no visible entry of ours" — files none of which
91
+ // are artifacts, or nothing but dot-named entries, the `loam seed` shape.)
92
+ const isLeafService = (dir) => !hasMarker(dir) && !hasArtifact(dir) && !dirChildren.has(dir);
93
+ const candidates = [...new Set([...fileChildren.keys(), ...dirChildren.keys(), ...bare])]
94
+ .filter((dir) => dir !== "services" && dir.startsWith("services/"))
95
+ .filter((dir) => hasArtifact(dir) || isLeafService(dir))
96
+ .sort((a, b) => {
97
+ const depth = a.split("/").length - b.split("/").length;
98
+ return depth !== 0 ? depth : a < b ? -1 : 1;
99
+ });
100
+ const accepted = [];
101
+ const acceptedDirs = new Set();
102
+ // Is this candidate INTERIOR to the nearest service already accepted above
103
+ // it? A plain service swallows everything beneath (the walk stops there); a
104
+ // marker-beside-artifacts service is descended except through its
105
+ // artifact-named children (`adrs/`), so a service stranded under it stays.
106
+ const interior = (segments) => {
107
+ for (let i = segments.length - 1; i >= 2; i -= 1) {
108
+ const ancestor = segments.slice(0, i).join("/");
109
+ if (!acceptedDirs.has(ancestor))
110
+ continue;
111
+ return hasMarker(ancestor) ? isServiceArtifactName(segments[i], "dir") : true;
112
+ }
113
+ return false;
114
+ };
115
+ for (const dir of candidates) {
116
+ const segments = dir.split("/");
117
+ if (interior(segments))
118
+ continue;
119
+ acceptedDirs.add(dir);
120
+ accepted.push({ id: segments[segments.length - 1], dir, files: fileChildren.get(dir) ?? new Set() });
121
+ }
122
+ return { dirs: accepted, ambiguous: collisions(accepted) };
123
+ }
124
+ function collisions(dirs) {
125
+ const byId = new Map();
126
+ for (const d of dirs)
127
+ byId.set(d.id, [...(byId.get(d.id) ?? []), d.dir]);
128
+ return new Map([...byId].filter(([, claimants]) => claimants.length > 1));
129
+ }
130
+ /**
131
+ * Lift one fetched file into an axis state through its parser. Absence is the
132
+ * listing's answer, a `show` failure is per-subject containment, and only
133
+ * then do the bytes reach a parser.
134
+ */
135
+ function axisFrom(raw, parse) {
136
+ if (raw === null)
137
+ return { kind: "absent" };
138
+ if (raw.read.kind === "failed")
139
+ return { kind: "unreadable", path: raw.rel, error: raw.read.detail };
140
+ return parse(raw.rel, raw.read.bytes);
141
+ }
142
+ /**
143
+ * The same refusal `readOpenapi`/`readAsyncapi`/`decodeDocument` make on the
144
+ * living side: non-UTF-8 bytes would be silently rewritten as U+FFFD by
145
+ * `toString`, and a document nobody wrote must not be graded.
146
+ */
147
+ function decodeBase(path, bytes) {
148
+ if (!isUtf8(bytes))
149
+ return { unreadable: { kind: "unreadable", path, error: "file is not valid UTF-8" } };
150
+ return { text: bytes.toString("utf8") };
151
+ }
152
+ function specState(path, bytes) {
153
+ const decoded = decodeBase(path, bytes);
154
+ if ("unreadable" in decoded)
155
+ return decoded.unreadable;
156
+ try {
157
+ return { kind: "read", value: parseRequirements(decoded.text) };
158
+ }
159
+ catch (e) {
160
+ return { kind: "unreadable", path, error: e instanceof Error ? e.message : String(e) };
161
+ }
162
+ }
163
+ function openapiState(path, bytes) {
164
+ const decoded = decodeBase(path, bytes);
165
+ if ("unreadable" in decoded)
166
+ return decoded.unreadable;
167
+ const doc = openapiFromText(decoded.text);
168
+ return doc.unreadable
169
+ ? { kind: "unreadable", path, error: doc.error ?? "not a readable OpenAPI document" }
170
+ : { kind: "read", value: doc.ops.filter((op) => !op.remove) };
171
+ }
172
+ function asyncapiState(path, bytes) {
173
+ const decoded = decodeBase(path, bytes);
174
+ if ("unreadable" in decoded)
175
+ return decoded.unreadable;
176
+ const doc = asyncapiFromText(decoded.text);
177
+ return doc.unreadable
178
+ ? { kind: "unreadable", path, error: doc.error ?? "not a readable AsyncAPI document" }
179
+ : { kind: "read", value: doc.messages.filter((m) => m.remove !== true) };
180
+ }
181
+ /** One base service's four axes. Sequential INSIDE the service on purpose: the pool below rations children at the service grain, and four spawns per in-flight worker would multiply past the cap it exists to hold. */
182
+ async function readBaseService(base, d) {
183
+ const fetch = async (file) => d.files.has(file) ? { rel: `${d.dir}/${file}`, read: await showBaseFile(base, `${d.dir}/${file}`) } : null;
184
+ return {
185
+ id: d.id,
186
+ dir: d.dir,
187
+ spec: axisFrom(await fetch(ARTIFACT_FILES.spec), specState),
188
+ archSpec: axisFrom(await fetch(ARTIFACT_FILES.archSpec), specState),
189
+ openapi: axisFrom(await fetch(ARTIFACT_FILES.openapi), openapiState),
190
+ asyncapi: axisFrom(await fetch(ARTIFACT_FILES.asyncapi), asyncapiState),
191
+ };
192
+ }
193
+ /**
194
+ * The whole base fleet. `failed` only when git cannot LIST the tree — the
195
+ * whole-diff refusal; a single file that cannot be shown or parsed degrades
196
+ * its own subject's axis to `unreadable` and nothing else.
197
+ */
198
+ export async function readBaseFleet(base) {
199
+ const listing = await listBaseTree(base);
200
+ if (listing.kind === "failed")
201
+ return listing;
202
+ const { dirs, ambiguous } = classifyBaseDirs(listing.paths);
203
+ const sorted = [...dirs].sort((a, b) => (a.id < b.id ? -1 : 1)).filter((d) => !ambiguous.has(d.id));
204
+ // Pooled, ordered reads: bounded children in flight, results by input index,
205
+ // so the same base state always assembles the same fleet.
206
+ const states = await inOrder(sorted, (d) => readBaseService(base, d));
207
+ return { kind: "ok", fleet: { services: new Map(states.map((s) => [s.id, s])), ambiguous } };
208
+ }
209
+ //# sourceMappingURL=base-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-state.js","sourceRoot":"","sources":["../../../src/core/diff/base-state.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The base side of `loam diff`: the fleet as the base commit held it, read\n * entirely out of git's object store (`./base-git.ts`) and parsed with the\n * SAME parsers the living files go through — `parseRequirements`,\n * `openapiFromText`, `asyncapiFromText` — so a base document can never read\n * differently from the identical bytes sitting on disk.\n *\n * CLASSIFICATION is a second, simplified spelling of `repo/tree/walk.ts`'s\n * rule, and that is a recorded risk, not an accident: the walk classifies a\n * LIVE filesystem (readdir, symlinks, marker files) and cannot walk a git\n * ref. What keeps the two honest is sharing the artifact-name table\n * (`ARTIFACT_FILES`/`isServiceArtifactName` from `repo/paths.ts`) and the\n * marker name (`SUBSYSTEM_MARKER`) instead of respelling them, and restating\n * — exactly — the walk's SERVICE branches: (1) a directory holding a service\n * artifact is a service, marker present or not (artifacts win); (2) the leaf\n * rule — neither marker nor artifact and nothing beneath (an empty or\n * not-yet-adopted directory, a lone README.md, a lone `.gitkeep` — which is\n * the whole content of a directory `loam seed` creates) is a service too; (3) nothing\n * inside a PLAIN service is classified again (`examples/openapi.yaml` two\n * levels down is interior, the walk stops there), but a service carrying the\n * marker BESIDE its artifacts is still descended — minus artifact-named\n * children like `adrs/` — so the services stranded beneath it stay\n * enumerated, the walk's marker-misplaced branch. Subsystem and unmarked\n * group directories need no restating: a flat file listing descends through\n * them by construction. What is deliberately NOT restated: the walk's\n * findings (`subsystem.marker-misplaced`, `subsystem.unmarked`,\n * `subsystem.name-collision`) — those are validate's live diagnostics; here\n * a name claimed twice suspends the subject (`ambiguous`) instead. If\n * walk.ts's rule ever changes, this module owes the same change;\n * test/diff.test.ts pins the visible agreement (subsystem move, not-adopted\n * leaf, stranded-under-marker).\n *\n * Deliberate consequence of joining by leaf directory name: a service MOVED\n * between subsystems keeps its id and diffs as UNCHANGED. Subsystem placement\n * is navigation, not identity — SCHEMA's own position, and the reason\n * `subsystem move` never touches a join key.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { inOrder } from \"../kernel/concurrency.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport type { Requirement } from \"../document/spec.js\";\nimport { openapiFromText, type Operation } from \"../openapi/doc.js\";\nimport { asyncapiFromText } from \"../asyncapi/read.js\";\nimport type { EventMessage } from \"../asyncapi/model.js\";\nimport { ARTIFACT_FILES, isServiceArtifactName } from \"../repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../repo/tree/marker.js\";\nimport { listBaseTree, showBaseFile, type ResolvedBase } from \"./base-git.js\";\n\n/**\n * One artifact's state on one side of the diff. `unreadable` is the\n * containment arm: the file exists but could not be read as its kind, so\n * every finding on this axis for this subject is SUSPENDED — \"nobody could\n * look\" must never grade as \"nothing is there\", in either direction.\n */\nexport type AxisState<T> =\n | { kind: \"absent\" }\n | { kind: \"read\"; value: T }\n | { kind: \"unreadable\"; path: string; error: string };\n\n/** The keys of a service state that hold requirement-bearing documents. */\nexport type SpecAxisKey = \"spec\" | \"archSpec\";\n\n/**\n * One service on one side of the diff — the same shape for the base ref and\n * the working tree (`./current-state.ts` builds the other), which is what\n * lets `./semantic.ts` join the two without caring which side came from git.\n */\nexport interface ServiceState {\n id: string;\n /** Docs-relative directory with forward slashes, e.g. `services/billing/payment-service`. */\n dir: string;\n spec: AxisState<Requirement[]>;\n /** Read on both sides for the `Consumes:` join — the outbox requirement lives here as often as in spec.md (core/coherence/events/lookups.ts's lesson). */\n archSpec: AxisState<Requirement[]>;\n /** Operations with `x-loam-remove` markers filtered out: a marker is bookkeeping, never a callable operation. */\n openapi: AxisState<Operation[]>;\n /** Messages with removal markers filtered out, for the same reason. */\n asyncapi: AxisState<EventMessage[]>;\n}\n\nexport interface BaseFleet {\n /** By service id, insertion-sorted by id — deterministic output depends on it. */\n services: Map<string, ServiceState>;\n /**\n * Ids claimed by MORE than one base directory. The live walk grades this\n * `subsystem.name-collision`; here the id's base state is unanswerable, so\n * the subject is carried with every axis suspended rather than one claimant\n * silently winning the join.\n */\n ambiguous: Map<string, string[]>;\n}\n\nexport type BaseFleetRead =\n | { kind: \"ok\"; fleet: BaseFleet }\n | { kind: \"failed\"; detail: string };\n\n/** A classified base service directory and which artifact files the base tree listed in it. */\ninterface BaseDir {\n id: string;\n dir: string;\n files: Set<string>;\n}\n\n/**\n * Classify the base tree's file listing into service directories — the module\n * banner's three restated walk branches, in depth order so every ancestor is\n * decided before anything beneath it.\n */\nfunction classifyBaseDirs(paths: string[]): { dirs: BaseDir[]; ambiguous: Map<string, string[]> } {\n const fileChildren = new Map<string, Set<string>>();\n const dirChildren = new Map<string, Set<string>>();\n // Directories nothing VISIBLE lives in, so neither map above names them.\n const bare = new Set<string>();\n for (const path of paths) {\n const all = path.split(\"/\");\n // Dot-named entries are invisible to the live walk — but the walk still\n // SEES the directory holding them, because readdir discovered the\n // directory itself and only its CONTENTS are filtered (walk.ts's\n // `subdirNames` and `visitDir`). So cut the path at its first dot-named\n // segment rather than discarding the whole path: everything above the cut\n // stays a visible directory, the cut segment and everything beneath it\n // stay invisible. Discarding it outright hid every directory whose only\n // file is dot-named — which is exactly what `loam seed` writes into a\n // service directory it creates (`.gitkeep`, and nothing else), so a seeded\n // service was absent from the base and diffed as `diff.service-added`\n // against the unchanged tree that had just been committed.\n const cut = all.findIndex((s) => s.startsWith(\".\") || s === \"\");\n const segments = cut === -1 ? all : all.slice(0, cut);\n // Only a whole path ends in a file; a cut one is a pure directory chain.\n const fileAt = cut === -1 ? segments.length - 1 : -1;\n for (let i = 1; i < segments.length; i += 1) {\n const dir = segments.slice(0, i).join(\"/\");\n const child = segments[i]!;\n const into = i === fileAt ? fileChildren : dirChildren;\n let set = into.get(dir);\n if (set === undefined) into.set(dir, (set = new Set()));\n set.add(child);\n }\n if (fileAt === -1 && segments.length > 1) bare.add(segments.join(\"/\"));\n }\n const hasMarker = (dir: string): boolean => fileChildren.get(dir)?.has(SUBSYSTEM_MARKER) === true;\n const hasArtifact = (dir: string): boolean =>\n [...(fileChildren.get(dir) ?? [])].some((f) => isServiceArtifactName(f, \"file\")) ||\n [...(dirChildren.get(dir) ?? [])].some((d) => isServiceArtifactName(d, \"dir\"));\n // The walk's leaf rule: neither marker nor artifact and nothing beneath is a\n // service. (A truly EMPTY directory is unrepresentable in a git tree, so at\n // the base this arm means \"no visible entry of ours\" — files none of which\n // are artifacts, or nothing but dot-named entries, the `loam seed` shape.)\n const isLeafService = (dir: string): boolean => !hasMarker(dir) && !hasArtifact(dir) && !dirChildren.has(dir);\n const candidates = [...new Set([...fileChildren.keys(), ...dirChildren.keys(), ...bare])]\n .filter((dir) => dir !== \"services\" && dir.startsWith(\"services/\"))\n .filter((dir) => hasArtifact(dir) || isLeafService(dir))\n .sort((a, b) => {\n const depth = a.split(\"/\").length - b.split(\"/\").length;\n return depth !== 0 ? depth : a < b ? -1 : 1;\n });\n const accepted: BaseDir[] = [];\n const acceptedDirs = new Set<string>();\n // Is this candidate INTERIOR to the nearest service already accepted above\n // it? A plain service swallows everything beneath (the walk stops there); a\n // marker-beside-artifacts service is descended except through its\n // artifact-named children (`adrs/`), so a service stranded under it stays.\n const interior = (segments: string[]): boolean => {\n for (let i = segments.length - 1; i >= 2; i -= 1) {\n const ancestor = segments.slice(0, i).join(\"/\");\n if (!acceptedDirs.has(ancestor)) continue;\n return hasMarker(ancestor) ? isServiceArtifactName(segments[i]!, \"dir\") : true;\n }\n return false;\n };\n for (const dir of candidates) {\n const segments = dir.split(\"/\");\n if (interior(segments)) continue;\n acceptedDirs.add(dir);\n accepted.push({ id: segments[segments.length - 1]!, dir, files: fileChildren.get(dir) ?? new Set() });\n }\n return { dirs: accepted, ambiguous: collisions(accepted) };\n}\n\nfunction collisions(dirs: BaseDir[]): Map<string, string[]> {\n const byId = new Map<string, string[]>();\n for (const d of dirs) byId.set(d.id, [...(byId.get(d.id) ?? []), d.dir]);\n return new Map([...byId].filter(([, claimants]) => claimants.length > 1));\n}\n\n/** One shown base file, or `null` for a file the base tree never listed. */\ntype Fetched = { rel: string; read: Awaited<ReturnType<typeof showBaseFile>> } | null;\n\n/**\n * Lift one fetched file into an axis state through its parser. Absence is the\n * listing's answer, a `show` failure is per-subject containment, and only\n * then do the bytes reach a parser.\n */\nfunction axisFrom<T>(raw: Fetched, parse: (path: string, bytes: Buffer) => AxisState<T>): AxisState<T> {\n if (raw === null) return { kind: \"absent\" };\n if (raw.read.kind === \"failed\") return { kind: \"unreadable\", path: raw.rel, error: raw.read.detail };\n return parse(raw.rel, raw.read.bytes);\n}\n\n/**\n * The same refusal `readOpenapi`/`readAsyncapi`/`decodeDocument` make on the\n * living side: non-UTF-8 bytes would be silently rewritten as U+FFFD by\n * `toString`, and a document nobody wrote must not be graded.\n */\nfunction decodeBase(path: string, bytes: Buffer): { text: string } | { unreadable: AxisState<never> } {\n if (!isUtf8(bytes)) return { unreadable: { kind: \"unreadable\", path, error: \"file is not valid UTF-8\" } };\n return { text: bytes.toString(\"utf8\") };\n}\n\nfunction specState(path: string, bytes: Buffer): AxisState<Requirement[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n try {\n return { kind: \"read\", value: parseRequirements(decoded.text) };\n } catch (e) {\n return { kind: \"unreadable\", path, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\nfunction openapiState(path: string, bytes: Buffer): AxisState<Operation[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n const doc = openapiFromText(decoded.text);\n return doc.unreadable\n ? { kind: \"unreadable\", path, error: doc.error ?? \"not a readable OpenAPI document\" }\n : { kind: \"read\", value: doc.ops.filter((op) => !op.remove) };\n}\n\nfunction asyncapiState(path: string, bytes: Buffer): AxisState<EventMessage[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n const doc = asyncapiFromText(decoded.text);\n return doc.unreadable\n ? { kind: \"unreadable\", path, error: doc.error ?? \"not a readable AsyncAPI document\" }\n : { kind: \"read\", value: doc.messages.filter((m) => m.remove !== true) };\n}\n\n/** One base service's four axes. Sequential INSIDE the service on purpose: the pool below rations children at the service grain, and four spawns per in-flight worker would multiply past the cap it exists to hold. */\nasync function readBaseService(base: ResolvedBase, d: BaseDir): Promise<ServiceState> {\n const fetch = async (file: string): Promise<Fetched> =>\n d.files.has(file) ? { rel: `${d.dir}/${file}`, read: await showBaseFile(base, `${d.dir}/${file}`) } : null;\n return {\n id: d.id,\n dir: d.dir,\n spec: axisFrom(await fetch(ARTIFACT_FILES.spec), specState),\n archSpec: axisFrom(await fetch(ARTIFACT_FILES.archSpec), specState),\n openapi: axisFrom(await fetch(ARTIFACT_FILES.openapi), openapiState),\n asyncapi: axisFrom(await fetch(ARTIFACT_FILES.asyncapi), asyncapiState),\n };\n}\n\n/**\n * The whole base fleet. `failed` only when git cannot LIST the tree — the\n * whole-diff refusal; a single file that cannot be shown or parsed degrades\n * its own subject's axis to `unreadable` and nothing else.\n */\nexport async function readBaseFleet(base: ResolvedBase): Promise<BaseFleetRead> {\n const listing = await listBaseTree(base);\n if (listing.kind === \"failed\") return listing;\n const { dirs, ambiguous } = classifyBaseDirs(listing.paths);\n const sorted = [...dirs].sort((a, b) => (a.id < b.id ? -1 : 1)).filter((d) => !ambiguous.has(d.id));\n // Pooled, ordered reads: bounded children in flight, results by input index,\n // so the same base state always assembles the same fleet.\n const states = await inOrder(sorted, (d) => readBaseService(base, d));\n return { kind: \"ok\", fleet: { services: new Map(states.map((s) => [s.id, s])), ambiguous } };\n}\n"]}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The working-tree side of `loam diff`: the fleet as it stands on disk, read
3
+ * through the invocation's `FleetContext` into the SAME `ServiceState` shape
4
+ * `./base-state.ts` builds from git — one shape, so `./semantic.ts` joins the
5
+ * two sides without caring which one came from the object store.
6
+ *
7
+ * Containment discipline, per subject and per axis: an artifact that EXISTS
8
+ * but cannot be read (non-UTF-8 spec bytes, a directory sitting where a file
9
+ * belongs, broken contract YAML) degrades that one axis to `unreadable` with
10
+ * its path — never a thrown enumeration, never an empty list a diff would
11
+ * read as "everything was removed". Absence stays absence: a file deleted
12
+ * between the existence probe and the read is classified by its errno
13
+ * (ENOENT), the same TOCTOU discipline `loadConfig` documents.
14
+ */
15
+ import { existsSync } from "node:fs";
16
+ import { repoPath } from "../envelope/json.js";
17
+ import { servicePathsAt } from "../repo/paths.js";
18
+ import { inOrder } from "../kernel/concurrency.js";
19
+ function readFailure(e, rel) {
20
+ const errno = e;
21
+ if (errno.code === "ENOENT")
22
+ return { kind: "absent" };
23
+ return { kind: "unreadable", path: rel, error: e instanceof Error ? e.message : String(e) };
24
+ }
25
+ /** One spec-axis file (`spec.md` / `arch.spec.md`) as parsed requirements. */
26
+ async function requirementsAxis(context, abs, rel) {
27
+ if (!existsSync(abs))
28
+ return { kind: "absent" };
29
+ try {
30
+ return { kind: "read", value: await context.readRequirements(abs) };
31
+ }
32
+ catch (e) {
33
+ // decodeDocument's non-UTF-8 refusal lands here too: bytes that would
34
+ // parse as zero requirements must suspend the axis, not empty it.
35
+ return readFailure(e, rel);
36
+ }
37
+ }
38
+ async function openapiAxis(context, abs, rel) {
39
+ if (!existsSync(abs))
40
+ return { kind: "absent" };
41
+ try {
42
+ const doc = await context.readOpenapi(abs);
43
+ return doc.unreadable
44
+ ? { kind: "unreadable", path: rel, error: doc.error ?? "not a readable OpenAPI document" }
45
+ : { kind: "read", value: doc.ops.filter((op) => !op.remove) };
46
+ }
47
+ catch (e) {
48
+ return readFailure(e, rel);
49
+ }
50
+ }
51
+ async function asyncapiAxis(context, abs, rel) {
52
+ if (!existsSync(abs))
53
+ return { kind: "absent" };
54
+ try {
55
+ const doc = await context.readAsyncapi(abs);
56
+ return doc.unreadable
57
+ ? { kind: "unreadable", path: rel, error: doc.error ?? "not a readable AsyncAPI document" }
58
+ : { kind: "read", value: doc.messages.filter((m) => m.remove !== true) };
59
+ }
60
+ catch (e) {
61
+ return readFailure(e, rel);
62
+ }
63
+ }
64
+ /**
65
+ * The current fleet, one `ServiceState` per enumerated service. The
66
+ * enumeration itself (the tree walk) may throw `DocsRepoUnavailableError` —
67
+ * that is the whole-repo refusal `docsRepoReady` fronts, not a per-subject
68
+ * degradation, and it stays a throw for the command layer to map.
69
+ */
70
+ export async function readCurrentFleet(docsDir, context) {
71
+ const entries = await context.listServices(docsDir);
72
+ const states = await inOrder(entries, async (entry) => {
73
+ const paths = servicePathsAt(entry.dir);
74
+ return {
75
+ id: entry.id,
76
+ dir: repoPath(docsDir, entry.dir),
77
+ spec: await requirementsAxis(context, paths.spec, repoPath(docsDir, paths.spec)),
78
+ archSpec: await requirementsAxis(context, paths.archSpec, repoPath(docsDir, paths.archSpec)),
79
+ openapi: await openapiAxis(context, paths.openapi, repoPath(docsDir, paths.openapi)),
80
+ asyncapi: await asyncapiAxis(context, paths.asyncapi, repoPath(docsDir, paths.asyncapi)),
81
+ };
82
+ });
83
+ const ambiguous = currentCollisions(states);
84
+ return {
85
+ services: new Map(states.filter((s) => !ambiguous.has(s.id)).map((s) => [s.id, s])),
86
+ ambiguous,
87
+ };
88
+ }
89
+ /** Ids claimed by more than one enumerated directory, with every claimant named. */
90
+ function currentCollisions(states) {
91
+ const byId = new Map();
92
+ for (const s of states)
93
+ byId.set(s.id, [...(byId.get(s.id) ?? []), s.dir]);
94
+ return new Map([...byId].filter(([, dirs]) => dirs.length > 1));
95
+ }
96
+ //# sourceMappingURL=current-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-state.js","sourceRoot":"","sources":["../../../src/core/diff/current-state.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The working-tree side of `loam diff`: the fleet as it stands on disk, read\n * through the invocation's `FleetContext` into the SAME `ServiceState` shape\n * `./base-state.ts` builds from git — one shape, so `./semantic.ts` joins the\n * two sides without caring which one came from the object store.\n *\n * Containment discipline, per subject and per axis: an artifact that EXISTS\n * but cannot be read (non-UTF-8 spec bytes, a directory sitting where a file\n * belongs, broken contract YAML) degrades that one axis to `unreadable` with\n * its path — never a thrown enumeration, never an empty list a diff would\n * read as \"everything was removed\". Absence stays absence: a file deleted\n * between the existence probe and the read is classified by its errno\n * (ENOENT), the same TOCTOU discipline `loadConfig` documents.\n */\nimport { existsSync } from \"node:fs\";\nimport type { Requirement } from \"../document/spec.js\";\nimport type { Operation } from \"../openapi/doc.js\";\nimport type { EventMessage } from \"../asyncapi/model.js\";\nimport { repoPath } from \"../envelope/json.js\";\nimport { servicePathsAt } from \"../repo/paths.js\";\nimport { inOrder } from \"../kernel/concurrency.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { AxisState, ServiceState } from \"./base-state.js\";\n\nexport interface CurrentFleet {\n /** By service id, in the enumeration's (sorted-walk) order. Ids the `ambiguous` map claims are excluded. */\n services: Map<string, ServiceState>;\n /**\n * Ids more than one working-tree directory claims — `subsystem.name-collision`'s\n * live shape, which validate diagnoses. Diffing either claimant would pick a\n * winner nobody chose (a `new Map` collapse here used to keep the LAST one and\n * fabricate a whole change story about a service that never moved), so the\n * subject is suspended exactly as the base side's `ambiguous` map suspends it.\n */\n ambiguous: Map<string, string[]>;\n}\n\nfunction readFailure(e: unknown, rel: string): { kind: \"absent\" } | { kind: \"unreadable\"; path: string; error: string } {\n const errno = e as NodeJS.ErrnoException;\n if (errno.code === \"ENOENT\") return { kind: \"absent\" };\n return { kind: \"unreadable\", path: rel, error: e instanceof Error ? e.message : String(e) };\n}\n\n/** One spec-axis file (`spec.md` / `arch.spec.md`) as parsed requirements. */\nasync function requirementsAxis(context: FleetContext, abs: string, rel: string): Promise<AxisState<Requirement[]>> {\n if (!existsSync(abs)) return { kind: \"absent\" };\n try {\n return { kind: \"read\", value: await context.readRequirements(abs) };\n } catch (e) {\n // decodeDocument's non-UTF-8 refusal lands here too: bytes that would\n // parse as zero requirements must suspend the axis, not empty it.\n return readFailure(e, rel);\n }\n}\n\nasync function openapiAxis(context: FleetContext, abs: string, rel: string): Promise<AxisState<Operation[]>> {\n if (!existsSync(abs)) return { kind: \"absent\" };\n try {\n const doc = await context.readOpenapi(abs);\n return doc.unreadable\n ? { kind: \"unreadable\", path: rel, error: doc.error ?? \"not a readable OpenAPI document\" }\n : { kind: \"read\", value: doc.ops.filter((op) => !op.remove) };\n } catch (e) {\n return readFailure(e, rel);\n }\n}\n\nasync function asyncapiAxis(context: FleetContext, abs: string, rel: string): Promise<AxisState<EventMessage[]>> {\n if (!existsSync(abs)) return { kind: \"absent\" };\n try {\n const doc = await context.readAsyncapi(abs);\n return doc.unreadable\n ? { kind: \"unreadable\", path: rel, error: doc.error ?? \"not a readable AsyncAPI document\" }\n : { kind: \"read\", value: doc.messages.filter((m) => m.remove !== true) };\n } catch (e) {\n return readFailure(e, rel);\n }\n}\n\n/**\n * The current fleet, one `ServiceState` per enumerated service. The\n * enumeration itself (the tree walk) may throw `DocsRepoUnavailableError` —\n * that is the whole-repo refusal `docsRepoReady` fronts, not a per-subject\n * degradation, and it stays a throw for the command layer to map.\n */\nexport async function readCurrentFleet(docsDir: DocsDir, context: FleetContext): Promise<CurrentFleet> {\n const entries = await context.listServices(docsDir);\n const states = await inOrder(entries, async (entry): Promise<ServiceState> => {\n const paths = servicePathsAt(entry.dir);\n return {\n id: entry.id,\n dir: repoPath(docsDir, entry.dir),\n spec: await requirementsAxis(context, paths.spec, repoPath(docsDir, paths.spec)),\n archSpec: await requirementsAxis(context, paths.archSpec, repoPath(docsDir, paths.archSpec)),\n openapi: await openapiAxis(context, paths.openapi, repoPath(docsDir, paths.openapi)),\n asyncapi: await asyncapiAxis(context, paths.asyncapi, repoPath(docsDir, paths.asyncapi)),\n };\n });\n const ambiguous = currentCollisions(states);\n return {\n services: new Map(states.filter((s) => !ambiguous.has(s.id)).map((s) => [s.id, s])),\n ambiguous,\n };\n}\n\n/** Ids claimed by more than one enumerated directory, with every claimant named. */\nfunction currentCollisions(states: readonly ServiceState[]): Map<string, string[]> {\n const byId = new Map<string, string[]>();\n for (const s of states) byId.set(s.id, [...(byId.get(s.id) ?? []), s.dir]);\n return new Map([...byId].filter(([, dirs]) => dirs.length > 1));\n}\n"]}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Which targets a base git ref puts in scope — the narrowing behind
3
+ * `loam validate --all --base <ref>`.
4
+ *
5
+ * The defect this exists for: a partly-adopted fleet has no CI setting that is
6
+ * both passing today and tightening over time. `--all` is green over eleven
7
+ * undocumented services (they warn, and warnings do not gate) and `--strict` is
8
+ * red from the first minute of adoption until the last boundary is written, so
9
+ * a team installs either a gate that never notices adoption stalling at 8% or
10
+ * one it cannot turn on. `--base` is the third setting: grade what this branch
11
+ * TOUCHED, at full `--strict` severity if the caller wants it, and let the
12
+ * untouched majority stay untouched.
13
+ *
14
+ * A SUB-PACKAGE of `core/diff/` rather than a sixth file in it, because that
15
+ * directory is at the five-file limit and because this is a different subject
16
+ * from the semantic diff beside it: `diff` compares two states of the fleet and
17
+ * reports what moved, this one only decides WHICH SUBJECTS a run should look
18
+ * at. Nothing here reads a document.
19
+ *
20
+ * What it does NOT do, and the line is not negotiable: a diff-scoped validate
21
+ * cannot see a victim boundary the change did not touch. Removing an operation
22
+ * from `payment-service` puts `payment-service` in scope and leaves
23
+ * `checkout-web` — whose living requirement still names the operation — out of
24
+ * it. That join is `loam diff --base`'s question and it stays `loam diff`'s;
25
+ * the two gates answer "did you document what you touched" and "did you break a
26
+ * consumer", and neither substitutes for the other. Selling a green here that
27
+ * hides a broken join would be worse than shipping no flag at all.
28
+ */
29
+ import { declineDetail, gitText } from "../../provenance/git.js";
30
+ import { repoPath } from "../../envelope/json.js";
31
+ import { resolveBase } from "../base-git.js";
32
+ /**
33
+ * Every path that differs between `ref` and the WORKING TREE, docs-relative.
34
+ *
35
+ * Two questions, not one, and the second is not optional: `git diff` compares
36
+ * the tree git tracks, so a service adopted five minutes ago — a brand new
37
+ * `services/<id>/spec.md` nobody has committed — is invisible to it. `validate`
38
+ * grades the working tree, so the scope has to be the working tree's, and an
39
+ * untracked new boundary that the run then skipped would be the flag's very
40
+ * first user reporting that it graded nothing.
41
+ *
42
+ * `--no-renames` on purpose: with rename detection on (git's default since
43
+ * 2.9) a service moved into a subsystem reports only its DESTINATION, and the
44
+ * checks that grade the fleet from the old placement would be out of scope.
45
+ * Two paths for a move is the honest answer.
46
+ *
47
+ * `-z` because git quotes non-ASCII and space-bearing paths in line output —
48
+ * the same reason `gitTrackedFiles` asks that way, and the same phantom
49
+ * directory named `"src` if it did not.
50
+ */
51
+ async function changedPaths(docsDir, commit, prefix) {
52
+ // Both run from docsDir. `git diff` ignores cwd and answers for the whole
53
+ // repository with root-relative paths; `git ls-files` restricts itself to
54
+ // cwd, and `--full-name` makes ITS paths root-relative too — so one prefix
55
+ // strip below is correct for both, exactly as `listBaseTree` needs one.
56
+ const tracked = await gitText(docsDir, ["diff", "--name-only", "--no-renames", "-z", commit, "--"]);
57
+ if (tracked.spawnError !== undefined || tracked.code !== 0 || tracked.overflowed) {
58
+ return { detail: tracked.overflowed ? "the changed-path listing exceeded the output cap." : declineDetail(tracked) };
59
+ }
60
+ const untracked = await gitText(docsDir, ["ls-files", "--others", "--exclude-standard", "--full-name", "-z"]);
61
+ if (untracked.spawnError !== undefined || untracked.code !== 0 || untracked.overflowed) {
62
+ return { detail: untracked.overflowed ? "the untracked-path listing exceeded the output cap." : declineDetail(untracked) };
63
+ }
64
+ const seen = new Set();
65
+ for (const raw of [...tracked.out.split("\0"), ...untracked.out.split("\0")]) {
66
+ // A docs repo checked out as a subdirectory of a larger repository answers
67
+ // in the whole repository's paths; anything outside the docs dir is
68
+ // somebody else's change and is not a target here.
69
+ if (raw === "" || !raw.startsWith(prefix))
70
+ continue;
71
+ const rel = raw.slice(prefix.length);
72
+ if (rel !== "")
73
+ seen.add(rel);
74
+ }
75
+ return [...seen].sort();
76
+ }
77
+ /**
78
+ * Is `path` inside `dir`? Both docs-relative, forward-slashed.
79
+ *
80
+ * A prefix test on the directory plus a separator, never a split on "/" and a
81
+ * read of the second segment: a service filed under a subsystem lives at
82
+ * `services/platform/payment-service/`, not at `services/<id>/`, and a
83
+ * path-splitting implementation drops every filed service out of scope
84
+ * SILENTLY — the run stays green because it graded nothing, which is the exact
85
+ * failure this flag is not allowed to have. The separator is what stops
86
+ * `services/pay` from claiming `services/payments/spec.md`.
87
+ */
88
+ function inside(path, dir) {
89
+ return dir !== "" && (path === dir || path.startsWith(`${dir}/`));
90
+ }
91
+ /** The fleet's own documents — the landscape target's subject, which owns no service directory. */
92
+ const ARCHITECTURE_DIR = "architecture";
93
+ /**
94
+ * Narrow a `--all` run to what changed since `ref`.
95
+ *
96
+ * Targets are matched through the ENUMERATIONS' own directories
97
+ * (`ServiceEntry.dir`, `FeatureEntry.dir`), turned docs-relative by the same
98
+ * `repoPath` the JSON contract uses. That is the whole reason the enumerations
99
+ * are passed in rather than re-derived: they already know where each service
100
+ * sits, at whatever depth, and asking them is what makes a filed fleet work.
101
+ */
102
+ export async function scopeSince(request) {
103
+ const { docsDir, ref } = request;
104
+ const base = await resolveBase(docsDir, ref);
105
+ if (base.kind === "no-git") {
106
+ return {
107
+ kind: "refused",
108
+ code: "repository-unavailable",
109
+ message: `The docs repo at ${docsDir} is not somewhere git can answer from (${base.detail}) — ` +
110
+ `--base narrows the run to what changed since a ref, which it reads with read-only git questions ` +
111
+ `(rev-parse/diff/ls-files). Clone the docs repo with its history, then re-run — or drop --base to grade every target.`,
112
+ };
113
+ }
114
+ if (base.kind === "no-ref") {
115
+ return {
116
+ kind: "refused",
117
+ code: "unknown-target",
118
+ message: `--base ${ref} does not resolve to a commit in the docs repo at ${docsDir}` +
119
+ `${base.detail === "" ? "" : ` (${base.detail})`}. ` +
120
+ `Pass a ref that exists there — main, origin/main, a tag or a commit sha — fetching first if it lives on the remote.`,
121
+ };
122
+ }
123
+ const changed = await changedPaths(docsDir, base.commit, base.prefix);
124
+ if (!Array.isArray(changed)) {
125
+ // Git resolved the ref and then would not say what moved. There is no
126
+ // partial answer to give: a scope built from half an answer grades a
127
+ // subset nobody chose and reports it as the branch's whole footprint.
128
+ return {
129
+ kind: "refused",
130
+ code: "repository-unavailable",
131
+ message: `git could not list what changed since ${ref} in the docs repo at ${docsDir} (${changed.detail}) — ` +
132
+ `without that list there is no honest scope, and a run over a guessed subset would report a green ` +
133
+ `for targets nobody graded. Re-run once git can answer, or drop --base to grade every target.`,
134
+ };
135
+ }
136
+ const dirOf = (abs) => repoPath(docsDir, abs);
137
+ return {
138
+ kind: "scope",
139
+ ref,
140
+ commit: base.commit,
141
+ // `architecture/` is the landscape target's whole directory: the fleet map,
142
+ // the permission/capability/obligation vocabularies and the fleet ADRs all
143
+ // live there, and every one of them is graded by the landscape target
144
+ // rather than by any service.
145
+ landscape: changed.some((path) => inside(path, ARCHITECTURE_DIR)),
146
+ services: request.services.filter((svc) => changed.some((path) => inside(path, dirOf(svc.dir)))),
147
+ features: request.features.filter((feat) => changed.some((path) => inside(path, dirOf(feat.dir)))),
148
+ totals: { services: request.services.length, features: request.features.length },
149
+ };
150
+ }
151
+ /**
152
+ * The `scope` object the `--json` payload carries — the run saying out loud
153
+ * what it looked at.
154
+ *
155
+ * Additive, and emitted only under `--base`, so no consumer of today's payload
156
+ * moves. It exists because the alternative is a payload that is byte-shaped
157
+ * like a whole-fleet run and is not one: `summary.services` would report two
158
+ * where the fleet has twelve, `valid: true` would read as a claim about the
159
+ * fleet, and nothing in the document would say otherwise.
160
+ */
161
+ export function scopeJson(scope) {
162
+ return {
163
+ base: scope.ref,
164
+ commit: scope.commit,
165
+ landscape: scope.landscape,
166
+ services: scope.services.map((svc) => svc.id),
167
+ features: scope.features.map((feat) => feat.id),
168
+ totals: scope.totals,
169
+ };
170
+ }
171
+ /**
172
+ * The one line both the text view and a reader of a CI log need: how much of
173
+ * the fleet this run actually graded, and against what.
174
+ *
175
+ * The "nothing was graded" clause is the load-bearing half. A `--base` run with
176
+ * no targets in scope has nothing to report and exits 0 — correctly, the branch
177
+ * touched no governed boundary — and a 0 that reads like a green over the
178
+ * system is the exact failure the docs-repo readiness gate exists to prevent.
179
+ * So the sentence says it, rather than letting an empty report say nothing.
180
+ */
181
+ export function scopeLine(scope) {
182
+ const graded = scope.services.length + scope.features.length + (scope.landscape ? 1 : 0);
183
+ return (`${scope.services.length} of ${scope.totals.services} services, ` +
184
+ `${scope.features.length} of ${scope.totals.features} features in scope since ${scope.ref}` +
185
+ (scope.landscape ? ", plus the landscape" : "") +
186
+ (graded === 0 ? " — nothing was graded" : ""));
187
+ }
188
+ //# sourceMappingURL=changed-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changed-paths.js","sourceRoot":"","sources":["../../../../src/core/diff/scope/changed-paths.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which targets a base git ref puts in scope — the narrowing behind\n * `loam validate --all --base <ref>`.\n *\n * The defect this exists for: a partly-adopted fleet has no CI setting that is\n * both passing today and tightening over time. `--all` is green over eleven\n * undocumented services (they warn, and warnings do not gate) and `--strict` is\n * red from the first minute of adoption until the last boundary is written, so\n * a team installs either a gate that never notices adoption stalling at 8% or\n * one it cannot turn on. `--base` is the third setting: grade what this branch\n * TOUCHED, at full `--strict` severity if the caller wants it, and let the\n * untouched majority stay untouched.\n *\n * A SUB-PACKAGE of `core/diff/` rather than a sixth file in it, because that\n * directory is at the five-file limit and because this is a different subject\n * from the semantic diff beside it: `diff` compares two states of the fleet and\n * reports what moved, this one only decides WHICH SUBJECTS a run should look\n * at. Nothing here reads a document.\n *\n * What it does NOT do, and the line is not negotiable: a diff-scoped validate\n * cannot see a victim boundary the change did not touch. Removing an operation\n * from `payment-service` puts `payment-service` in scope and leaves\n * `checkout-web` — whose living requirement still names the operation — out of\n * it. That join is `loam diff --base`'s question and it stays `loam diff`'s;\n * the two gates answer \"did you document what you touched\" and \"did you break a\n * consumer\", and neither substitutes for the other. Selling a green here that\n * hides a broken join would be worse than shipping no flag at all.\n */\nimport { declineDetail, gitText } from \"../../provenance/git.js\";\nimport { repoPath, type ErrorCode } from \"../../envelope/json.js\";\nimport { resolveBase } from \"../base-git.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\nimport type { FeatureEntry, ServiceEntry } from \"../../repo/entries.js\";\n\n/** Everything the narrowing needs: where to ask, what to ask about, and what there is to narrow. */\nexport interface ScopeRequest {\n docsDir: DocsDir;\n /** The ref as the caller spelled it — echoed in every message and in the payload. */\n ref: string;\n /** The run's full service enumeration, at whatever depth the tree walk found each one. */\n services: readonly ServiceEntry[];\n /** The run's full ACTIVE feature enumeration. */\n features: readonly FeatureEntry[];\n}\n\n/** The narrowed run: which targets the branch touched, and what it narrowed from. */\nexport interface ValidateScope {\n kind: \"scope\";\n ref: string;\n /** The 40-hex commit `ref` resolved to — what a rerun would have to pass to reproduce this scope. */\n commit: string;\n /** Did `architecture/` change? The landscape target is the fleet's own subject and has no directory of its own. */\n landscape: boolean;\n services: ServiceEntry[];\n features: FeatureEntry[];\n /** The denominators — what the run would have graded without `--base`. */\n totals: { services: number; features: number };\n}\n\n/**\n * A refusal, already spelled. The code is one `loam diff` already emits for the\n * same two conditions and the message is in the same register, deliberately: a\n * second stable code for \"that ref does not resolve\" would be a second thing to\n * document, branch on and keep in step, for a condition that is identical.\n *\n * The prose is built here rather than in the command because it is the same\n * sentence for every caller and because `base-git.ts`'s `detail` — git's own\n * words, bounded — is what makes it diagnosable; a command that only received a\n * `kind` would have to reinvent the sentence to say why.\n */\nexport interface ScopeRefusal {\n kind: \"refused\";\n code: ErrorCode;\n message: string;\n}\n\nexport type ScopeOutcome = ValidateScope | ScopeRefusal;\n\n/**\n * Every path that differs between `ref` and the WORKING TREE, docs-relative.\n *\n * Two questions, not one, and the second is not optional: `git diff` compares\n * the tree git tracks, so a service adopted five minutes ago — a brand new\n * `services/<id>/spec.md` nobody has committed — is invisible to it. `validate`\n * grades the working tree, so the scope has to be the working tree's, and an\n * untracked new boundary that the run then skipped would be the flag's very\n * first user reporting that it graded nothing.\n *\n * `--no-renames` on purpose: with rename detection on (git's default since\n * 2.9) a service moved into a subsystem reports only its DESTINATION, and the\n * checks that grade the fleet from the old placement would be out of scope.\n * Two paths for a move is the honest answer.\n *\n * `-z` because git quotes non-ASCII and space-bearing paths in line output —\n * the same reason `gitTrackedFiles` asks that way, and the same phantom\n * directory named `\"src` if it did not.\n */\nasync function changedPaths(docsDir: DocsDir, commit: string, prefix: string): Promise<string[] | { detail: string }> {\n // Both run from docsDir. `git diff` ignores cwd and answers for the whole\n // repository with root-relative paths; `git ls-files` restricts itself to\n // cwd, and `--full-name` makes ITS paths root-relative too — so one prefix\n // strip below is correct for both, exactly as `listBaseTree` needs one.\n const tracked = await gitText(docsDir, [\"diff\", \"--name-only\", \"--no-renames\", \"-z\", commit, \"--\"]);\n if (tracked.spawnError !== undefined || tracked.code !== 0 || tracked.overflowed) {\n return { detail: tracked.overflowed ? \"the changed-path listing exceeded the output cap.\" : declineDetail(tracked) };\n }\n const untracked = await gitText(docsDir, [\"ls-files\", \"--others\", \"--exclude-standard\", \"--full-name\", \"-z\"]);\n if (untracked.spawnError !== undefined || untracked.code !== 0 || untracked.overflowed) {\n return { detail: untracked.overflowed ? \"the untracked-path listing exceeded the output cap.\" : declineDetail(untracked) };\n }\n const seen = new Set<string>();\n for (const raw of [...tracked.out.split(\"\\0\"), ...untracked.out.split(\"\\0\")]) {\n // A docs repo checked out as a subdirectory of a larger repository answers\n // in the whole repository's paths; anything outside the docs dir is\n // somebody else's change and is not a target here.\n if (raw === \"\" || !raw.startsWith(prefix)) continue;\n const rel = raw.slice(prefix.length);\n if (rel !== \"\") seen.add(rel);\n }\n return [...seen].sort();\n}\n\n/**\n * Is `path` inside `dir`? Both docs-relative, forward-slashed.\n *\n * A prefix test on the directory plus a separator, never a split on \"/\" and a\n * read of the second segment: a service filed under a subsystem lives at\n * `services/platform/payment-service/`, not at `services/<id>/`, and a\n * path-splitting implementation drops every filed service out of scope\n * SILENTLY — the run stays green because it graded nothing, which is the exact\n * failure this flag is not allowed to have. The separator is what stops\n * `services/pay` from claiming `services/payments/spec.md`.\n */\nfunction inside(path: string, dir: string): boolean {\n return dir !== \"\" && (path === dir || path.startsWith(`${dir}/`));\n}\n\n/** The fleet's own documents — the landscape target's subject, which owns no service directory. */\nconst ARCHITECTURE_DIR = \"architecture\";\n\n/**\n * Narrow a `--all` run to what changed since `ref`.\n *\n * Targets are matched through the ENUMERATIONS' own directories\n * (`ServiceEntry.dir`, `FeatureEntry.dir`), turned docs-relative by the same\n * `repoPath` the JSON contract uses. That is the whole reason the enumerations\n * are passed in rather than re-derived: they already know where each service\n * sits, at whatever depth, and asking them is what makes a filed fleet work.\n */\nexport async function scopeSince(request: ScopeRequest): Promise<ScopeOutcome> {\n const { docsDir, ref } = request;\n const base = await resolveBase(docsDir, ref);\n if (base.kind === \"no-git\") {\n return {\n kind: \"refused\",\n code: \"repository-unavailable\",\n message:\n `The docs repo at ${docsDir} is not somewhere git can answer from (${base.detail}) — ` +\n `--base narrows the run to what changed since a ref, which it reads with read-only git questions ` +\n `(rev-parse/diff/ls-files). Clone the docs repo with its history, then re-run — or drop --base to grade every target.`,\n };\n }\n if (base.kind === \"no-ref\") {\n return {\n kind: \"refused\",\n code: \"unknown-target\",\n message:\n `--base ${ref} does not resolve to a commit in the docs repo at ${docsDir}` +\n `${base.detail === \"\" ? \"\" : ` (${base.detail})`}. ` +\n `Pass a ref that exists there — main, origin/main, a tag or a commit sha — fetching first if it lives on the remote.`,\n };\n }\n\n const changed = await changedPaths(docsDir, base.commit, base.prefix);\n if (!Array.isArray(changed)) {\n // Git resolved the ref and then would not say what moved. There is no\n // partial answer to give: a scope built from half an answer grades a\n // subset nobody chose and reports it as the branch's whole footprint.\n return {\n kind: \"refused\",\n code: \"repository-unavailable\",\n message:\n `git could not list what changed since ${ref} in the docs repo at ${docsDir} (${changed.detail}) — ` +\n `without that list there is no honest scope, and a run over a guessed subset would report a green ` +\n `for targets nobody graded. Re-run once git can answer, or drop --base to grade every target.`,\n };\n }\n\n const dirOf = (abs: string): string => repoPath(docsDir, abs);\n return {\n kind: \"scope\",\n ref,\n commit: base.commit,\n // `architecture/` is the landscape target's whole directory: the fleet map,\n // the permission/capability/obligation vocabularies and the fleet ADRs all\n // live there, and every one of them is graded by the landscape target\n // rather than by any service.\n landscape: changed.some((path) => inside(path, ARCHITECTURE_DIR)),\n services: request.services.filter((svc) => changed.some((path) => inside(path, dirOf(svc.dir)))),\n features: request.features.filter((feat) => changed.some((path) => inside(path, dirOf(feat.dir)))),\n totals: { services: request.services.length, features: request.features.length },\n };\n}\n\n/**\n * The `scope` object the `--json` payload carries — the run saying out loud\n * what it looked at.\n *\n * Additive, and emitted only under `--base`, so no consumer of today's payload\n * moves. It exists because the alternative is a payload that is byte-shaped\n * like a whole-fleet run and is not one: `summary.services` would report two\n * where the fleet has twelve, `valid: true` would read as a claim about the\n * fleet, and nothing in the document would say otherwise.\n */\nexport function scopeJson(scope: ValidateScope): Record<string, unknown> {\n return {\n base: scope.ref,\n commit: scope.commit,\n landscape: scope.landscape,\n services: scope.services.map((svc) => svc.id),\n features: scope.features.map((feat) => feat.id),\n totals: scope.totals,\n };\n}\n\n/**\n * The one line both the text view and a reader of a CI log need: how much of\n * the fleet this run actually graded, and against what.\n *\n * The \"nothing was graded\" clause is the load-bearing half. A `--base` run with\n * no targets in scope has nothing to report and exits 0 — correctly, the branch\n * touched no governed boundary — and a 0 that reads like a green over the\n * system is the exact failure the docs-repo readiness gate exists to prevent.\n * So the sentence says it, rather than letting an empty report say nothing.\n */\nexport function scopeLine(scope: ValidateScope): string {\n const graded = scope.services.length + scope.features.length + (scope.landscape ? 1 : 0);\n return (\n `${scope.services.length} of ${scope.totals.services} services, ` +\n `${scope.features.length} of ${scope.totals.features} features in scope since ${scope.ref}` +\n (scope.landscape ? \", plus the landscape\" : \"\") +\n (graded === 0 ? \" — nothing was graded\" : \"\")\n );\n}\n"]}