@ybotok/loam 0.1.0-beta.2 → 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 +2570 -61
  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 +2362 -245
  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 +82 -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/agent/agents-stamp.js +155 -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/read.js +239 -0
  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 +59 -55
  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 +132 -77
  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 -291
  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 -254
  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 -1591
  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 -343
  966. package/dist/commands/vouch.js.map +0 -1
  967. package/dist/core/agent.js +0 -2065
  968. package/dist/core/agent.js.map +0 -1
  969. package/dist/core/agents-stamp.js +0 -68
  970. package/dist/core/agents-stamp.js.map +0 -1
  971. package/dist/core/arch.js +0 -130
  972. package/dist/core/arch.js.map +0 -1
  973. package/dist/core/brief.js +0 -733
  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 -559
  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 -479
  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 -720
  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 -436
  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 -481
  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 -1104
  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,76 @@
1
+ /**
2
+ * One service's context pack: the exact docs slice bound to a service —
3
+ * living requirements verbatim, both contracts, the fleet edges one hop out,
4
+ * the permission and capability joins, provenance, and every active feature's
5
+ * delta over it — assembled as one deterministic payload an agent loads before
6
+ * working in that service's repository.
7
+ *
8
+ * Core rather than the command, exactly as `core/explore/explore.ts` sits
9
+ * behind `loam explore`: a future facade (an MCP tool, a second renderer)
10
+ * reuses the assembly without the printing, and the command layer stays I/O.
11
+ * Deterministic by construction — identical state must yield identical bytes:
12
+ * no timestamps, repo-relative forward-slash paths only, every unioned set
13
+ * sorted with `compareIds`, contract entries in document order (a fact of the
14
+ * file, hence of the state).
15
+ */
16
+ import { FleetContext } from "../fleet-context.js";
17
+ import { repoPath } from "../envelope/json.js";
18
+ import {} from "../repo/entries.js";
19
+ import { buildLiving } from "./living.js";
20
+ import { buildJoins } from "./joins.js";
21
+ import { buildFeatures } from "./features.js";
22
+ /**
23
+ * Assemble the pack. Reads only; writes nothing, and refuses nothing — every
24
+ * miss is a field, and an unreadable DOCUMENT is a flag the caller grades
25
+ * (`packHoles`). What does throw — an unreadable directory, bytes that are
26
+ * not UTF-8 — is the all-or-nothing class explore takes too: the honest
27
+ * answer is a refusal naming the path, not a pack with a silent hole.
28
+ */
29
+ export async function assemblePack(req) {
30
+ const { docsDir, entry, feature, context } = req;
31
+ // The three sections are independent derivations over one shared read
32
+ // index; identical reads share their in-flight promise, so the fan-out
33
+ // costs no duplicate I/O and no section can see a different file version.
34
+ const [living, joins, features] = await Promise.all([
35
+ buildLiving({ docsDir, entry, context }),
36
+ buildJoins({ docsDir, entry, context }),
37
+ buildFeatures({ docsDir, entry, feature, context }),
38
+ ]);
39
+ return {
40
+ service: entry.id,
41
+ path: repoPath(docsDir, entry.dir),
42
+ feature: feature === null ? null : feature.id,
43
+ ...living,
44
+ ...joins,
45
+ features,
46
+ };
47
+ }
48
+ /**
49
+ * Whether the pack has a silent hole: a document that exists but could not be
50
+ * read, so a section that LOOKS empty is really unanswered. The command maps
51
+ * this to exit 1 with `ok: true` — delta's precedent, same reasoning: the
52
+ * pack is consumed by agents that never ran validate, so the exit code must
53
+ * carry the failure itself. Set before the format fork; the guard is about
54
+ * the pack, not about how it is rendered.
55
+ */
56
+ export function packHoles(pack) {
57
+ return ((pack.landscape.present && !pack.landscape.parses) ||
58
+ pack.openapi.unreadable ||
59
+ pack.asyncapi.unreadable ||
60
+ // The two fleet vocabularies and the rollup's fleet walk fail the same
61
+ // way the contracts do: an unreadable file EMPTIES a section (`declared:
62
+ // false` on every permission, "(none realized here)") while looking like
63
+ // an answer, so their health is part of the hole contract, not decoration.
64
+ pack.permissionsVocabulary.invalid !== undefined ||
65
+ pack.capabilitiesVocabulary.invalid !== undefined ||
66
+ pack.capabilitiesUnread.length > 0 ||
67
+ // The use-case axis fails the same way, and one step wider than
68
+ // `landscape.parses` above: that flag is about `architecture/landscape.likec4`
69
+ // alone, while a flow lives in `architecture/usecases/*.likec4` and the whole
70
+ // directory is read as ONE LikeC4 project. So a use-case file with an
71
+ // unresolved element leaves `landscape.parses` true and empties every flow
72
+ // in the pack — the exact silent hole this predicate exists to refuse.
73
+ pack.useCaseScan.unreadable ||
74
+ pack.features.some((f) => f.architecture.errors.length > 0 || f.openapi.unreadable || f.events.unreadable));
75
+ }
76
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../../../src/core/pack/pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAwC,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAoB,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAmB,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,aAAa,EAAoB,MAAM,eAAe,CAAC;AA0BhE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAgB;IACjD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;IACjD,sEAAsE;IACtE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAClD,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACxC,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACvC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;KACpD,CAAC,CAAC;IACH,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,EAAE;QACjB,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC;QAClC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE;QAC7C,GAAG,MAAM;QACT,GAAG,KAAK;QACR,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAiB;IACzC,OAAO,CACL,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;QAClD,IAAI,CAAC,OAAO,CAAC,UAAU;QACvB,IAAI,CAAC,QAAQ,CAAC,UAAU;QACxB,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,2EAA2E;QAC3E,IAAI,CAAC,qBAAqB,CAAC,OAAO,KAAK,SAAS;QAChD,IAAI,CAAC,sBAAsB,CAAC,OAAO,KAAK,SAAS;QACjD,IAAI,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC;QAClC,gEAAgE;QAChE,+EAA+E;QAC/E,8EAA8E;QAC9E,sEAAsE;QACtE,2EAA2E;QAC3E,uEAAuE;QACvE,IAAI,CAAC,WAAW,CAAC,UAAU;QAC3B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAChB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CACvF,CACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * One service's context pack: the exact docs slice bound to a service —\n * living requirements verbatim, both contracts, the fleet edges one hop out,\n * the permission and capability joins, provenance, and every active feature's\n * delta over it — assembled as one deterministic payload an agent loads before\n * working in that service's repository.\n *\n * Core rather than the command, exactly as `core/explore/explore.ts` sits\n * behind `loam explore`: a future facade (an MCP tool, a second renderer)\n * reuses the assembly without the printing, and the command layer stays I/O.\n * Deterministic by construction — identical state must yield identical bytes:\n * no timestamps, repo-relative forward-slash paths only, every unioned set\n * sorted with `compareIds`, contract entries in document order (a fact of the\n * file, hence of the state).\n */\nimport { FleetContext } from \"../fleet-context.js\";\nimport { repoPath } from \"../envelope/json.js\";\nimport { type FeatureEntry, type ServiceEntry } from \"../repo/entries.js\";\nimport { buildLiving, type LivingSlice } from \"./living.js\";\nimport { buildJoins, type JoinsSlice } from \"./joins.js\";\nimport { buildFeatures, type PackFeature } from \"./features.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\nexport interface PackRequest {\n docsDir: DocsDir;\n /** The service, as the enumeration answered it — the entry, not an id and a dir (rule 17). */\n entry: ServiceEntry;\n /** The one feature `--feature` resolved, or null for every touching feature. */\n feature: FeatureEntry | null;\n context: FleetContext;\n}\n\nexport interface ContextPack extends LivingSlice, JoinsSlice {\n service: string;\n path: string;\n /**\n * The canonical id of the one feature `--feature` narrowed to, or null for\n * the everything-in-flight default. Echoed so a stored pack SAYS it is a\n * narrowed view: two packs of the same service can otherwise differ only by\n * what they silently left out, and an agent diffing them (or resuming from\n * one) has no way to tell a narrowed pack from a fleet gone quiet.\n */\n feature: string | null;\n features: PackFeature[];\n}\n\n/**\n * Assemble the pack. Reads only; writes nothing, and refuses nothing — every\n * miss is a field, and an unreadable DOCUMENT is a flag the caller grades\n * (`packHoles`). What does throw — an unreadable directory, bytes that are\n * not UTF-8 — is the all-or-nothing class explore takes too: the honest\n * answer is a refusal naming the path, not a pack with a silent hole.\n */\nexport async function assemblePack(req: PackRequest): Promise<ContextPack> {\n const { docsDir, entry, feature, context } = req;\n // The three sections are independent derivations over one shared read\n // index; identical reads share their in-flight promise, so the fan-out\n // costs no duplicate I/O and no section can see a different file version.\n const [living, joins, features] = await Promise.all([\n buildLiving({ docsDir, entry, context }),\n buildJoins({ docsDir, entry, context }),\n buildFeatures({ docsDir, entry, feature, context }),\n ]);\n return {\n service: entry.id,\n path: repoPath(docsDir, entry.dir),\n feature: feature === null ? null : feature.id,\n ...living,\n ...joins,\n features,\n };\n}\n\n/**\n * Whether the pack has a silent hole: a document that exists but could not be\n * read, so a section that LOOKS empty is really unanswered. The command maps\n * this to exit 1 with `ok: true` — delta's precedent, same reasoning: the\n * pack is consumed by agents that never ran validate, so the exit code must\n * carry the failure itself. Set before the format fork; the guard is about\n * the pack, not about how it is rendered.\n */\nexport function packHoles(pack: ContextPack): boolean {\n return (\n (pack.landscape.present && !pack.landscape.parses) ||\n pack.openapi.unreadable ||\n pack.asyncapi.unreadable ||\n // The two fleet vocabularies and the rollup's fleet walk fail the same\n // way the contracts do: an unreadable file EMPTIES a section (`declared:\n // false` on every permission, \"(none realized here)\") while looking like\n // an answer, so their health is part of the hole contract, not decoration.\n pack.permissionsVocabulary.invalid !== undefined ||\n pack.capabilitiesVocabulary.invalid !== undefined ||\n pack.capabilitiesUnread.length > 0 ||\n // The use-case axis fails the same way, and one step wider than\n // `landscape.parses` above: that flag is about `architecture/landscape.likec4`\n // alone, while a flow lives in `architecture/usecases/*.likec4` and the whole\n // directory is read as ONE LikeC4 project. So a use-case file with an\n // unresolved element leaves `landscape.parses` true and empties every flow\n // in the pack — the exact silent hole this predicate exists to refuse.\n pack.useCaseScan.unreadable ||\n pack.features.some(\n (f) => f.architecture.errors.length > 0 || f.openapi.unreadable || f.events.unreadable,\n )\n );\n}\n"]}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The fleet's authorization vocabulary: which permissions exist, and on which
3
+ * kind of subject each one is checked.
4
+ *
5
+ * It is a fleet document rather than a service one because permissions cross
6
+ * services in the shape that matters — a permission a profile holds is owned by
7
+ * whatever service owns profiles and enforced by every service that reads it,
8
+ * and a per-service copy would be N copies of one word. `architecture/` is
9
+ * where the fleet's shared documents already live.
10
+ *
11
+ * The unit is a PAIR, `<subject>/<permission>`, and that is the whole reason
12
+ * this file exists instead of a `Requires:` line joined against OpenAPI's
13
+ * `security`. OpenAPI carries permission NAMES only for oauth2 and
14
+ * openIdConnect — for `http bearer` and `apiKey` the spec requires the scope
15
+ * array to be empty — so a fleet on bearer tokens has nowhere in its contract
16
+ * to say what it checks. And `security` answers only for the CALLER, while a
17
+ * permission is as often checked on an entity inside the request.
18
+ *
19
+ * What this is NOT, deliberately: a domain model. Permissions are a flat list
20
+ * of names under a subject kind — no entities, no relations, no resources.
21
+ * Everything past that has no ground truth to check against, which is the same
22
+ * reason the capability axis stops at declared names
23
+ * (core/capabilities/capabilities.ts) and there is still no AUTHORED capability
24
+ * layer (SCHEMA.md) — a vocabulary loam cannot
25
+ * check is a vocabulary that drifts while looking authoritative. A permission
26
+ * that varies by resource is separate names (`channel:send:sms`,
27
+ * `channel:send:email`) or a column in a scenario's Examples table.
28
+ */
29
+ import { readFile } from "node:fs/promises";
30
+ import { existsSync } from "node:fs";
31
+ import { parse as parseYaml } from "yaml";
32
+ import { isRecord } from "../kernel/records.js";
33
+ const EMPTY = { present: false, subjects: [], byId: new Map() };
34
+ /**
35
+ * Read the vocabulary. A missing file is not a finding — a fleet that gates
36
+ * nothing on permissions owes no vocabulary, and the checks that read this stay
37
+ * silent on an absent one exactly as the OpenAPI axis stays silent for a
38
+ * service the landscape shows nobody calls.
39
+ */
40
+ export async function readVocabulary(path) {
41
+ if (!existsSync(path))
42
+ return EMPTY;
43
+ let doc;
44
+ try {
45
+ doc = parseYaml(await readFile(path, "utf8"));
46
+ }
47
+ catch (e) {
48
+ return { ...EMPTY, present: true, invalid: e instanceof Error ? e.message : String(e) };
49
+ }
50
+ if (doc === null || doc === undefined)
51
+ return { ...EMPTY, present: true };
52
+ if (!isRecord(doc))
53
+ return { ...EMPTY, present: true, invalid: "the document is not a mapping" };
54
+ const subjects = isRecord(doc["subjects"]) ? Object.keys(doc["subjects"]) : [];
55
+ const permsBlock = doc["permissions"];
56
+ if (permsBlock === undefined)
57
+ return { present: true, subjects, byId: new Map() };
58
+ if (!isRecord(permsBlock))
59
+ return { ...EMPTY, present: true, invalid: "`permissions` is not a mapping of subject kind to permissions" };
60
+ const byId = new Map();
61
+ for (const [subject, block] of Object.entries(permsBlock)) {
62
+ // A permission under an undeclared subject is the typo this shape exists to
63
+ // make impossible: `Requires: profiles/channel:send` against a `profile`
64
+ // block resolves nowhere, and without this the vocabulary would simply grow
65
+ // a second subject kind nobody meant to declare.
66
+ if (!subjects.includes(subject)) {
67
+ return { ...EMPTY, present: true, invalid: `'${subject}' holds permissions but is not declared under \`subjects\`` };
68
+ }
69
+ if (!isRecord(block)) {
70
+ return { ...EMPTY, present: true, invalid: `\`permissions.${subject}\` is not a mapping of permission name to its declaration` };
71
+ }
72
+ for (const [name, body] of Object.entries(block)) {
73
+ const decl = isRecord(body) ? body : {};
74
+ const enforced = decl["enforced_by"];
75
+ byId.set(`${subject}/${name}`, {
76
+ subject,
77
+ name,
78
+ id: `${subject}/${name}`,
79
+ ...(typeof decl["description"] === "string" ? { description: decl["description"] } : {}),
80
+ ...(typeof decl["owned_by"] === "string" ? { ownedBy: decl["owned_by"] } : {}),
81
+ enforcedBy: Array.isArray(enforced) ? enforced.filter((s) => typeof s === "string") : [],
82
+ });
83
+ }
84
+ }
85
+ return { present: true, subjects, byId };
86
+ }
87
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/core/permissions/permissions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The fleet's authorization vocabulary: which permissions exist, and on which\n * kind of subject each one is checked.\n *\n * It is a fleet document rather than a service one because permissions cross\n * services in the shape that matters — a permission a profile holds is owned by\n * whatever service owns profiles and enforced by every service that reads it,\n * and a per-service copy would be N copies of one word. `architecture/` is\n * where the fleet's shared documents already live.\n *\n * The unit is a PAIR, `<subject>/<permission>`, and that is the whole reason\n * this file exists instead of a `Requires:` line joined against OpenAPI's\n * `security`. OpenAPI carries permission NAMES only for oauth2 and\n * openIdConnect — for `http bearer` and `apiKey` the spec requires the scope\n * array to be empty — so a fleet on bearer tokens has nowhere in its contract\n * to say what it checks. And `security` answers only for the CALLER, while a\n * permission is as often checked on an entity inside the request.\n *\n * What this is NOT, deliberately: a domain model. Permissions are a flat list\n * of names under a subject kind — no entities, no relations, no resources.\n * Everything past that has no ground truth to check against, which is the same\n * reason the capability axis stops at declared names\n * (core/capabilities/capabilities.ts) and there is still no AUTHORED capability\n * layer (SCHEMA.md) — a vocabulary loam cannot\n * check is a vocabulary that drifts while looking authoritative. A permission\n * that varies by resource is separate names (`channel:send:sms`,\n * `channel:send:email`) or a column in a scenario's Examples table.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { parse as parseYaml } from \"yaml\";\nimport { isRecord } from \"../kernel/records.js\";\n\n/** One declared permission, under the subject kind it is checked on. */\nexport interface Permission {\n /** The subject kind — `user`, `profile`, `service`. */\n subject: string;\n /** The permission name as `Requires:` spells it, without the subject. */\n name: string;\n /** `<subject>/<name>` — the token a requirement writes. */\n id: string;\n description?: string;\n /**\n * Where the permission is DEFINED — a service id, or `idp` for a vocabulary\n * some identity provider owns. Never resolved: see `UNCHECKED`. It is here\n * because \"who would change this\" is the first question a reader asks, and a\n * blank is a worse answer than an unverified one.\n */\n ownedBy?: string;\n /** Services that CHECK it. Never resolved, for the same reason. */\n enforcedBy: string[];\n}\n\nexport interface Vocabulary {\n /** False when `architecture/permissions.yaml` does not exist — not an error. */\n present: boolean;\n /** Why the file could not be read as a vocabulary, when it could not. */\n invalid?: string;\n /** Declared subject kinds, in declaration order. */\n subjects: string[];\n /** Declared permissions, keyed by `<subject>/<name>`. */\n byId: Map<string, Permission>;\n}\n\nconst EMPTY: Vocabulary = { present: false, subjects: [], byId: new Map() };\n\n/**\n * Read the vocabulary. A missing file is not a finding — a fleet that gates\n * nothing on permissions owes no vocabulary, and the checks that read this stay\n * silent on an absent one exactly as the OpenAPI axis stays silent for a\n * service the landscape shows nobody calls.\n */\nexport async function readVocabulary(path: string): Promise<Vocabulary> {\n if (!existsSync(path)) return EMPTY;\n let doc: unknown;\n try {\n doc = parseYaml(await readFile(path, \"utf8\"));\n } catch (e) {\n return { ...EMPTY, present: true, invalid: e instanceof Error ? e.message : String(e) };\n }\n if (doc === null || doc === undefined) return { ...EMPTY, present: true };\n if (!isRecord(doc)) return { ...EMPTY, present: true, invalid: \"the document is not a mapping\" };\n\n const subjects = isRecord(doc[\"subjects\"]) ? Object.keys(doc[\"subjects\"]) : [];\n const permsBlock = doc[\"permissions\"];\n if (permsBlock === undefined) return { present: true, subjects, byId: new Map() };\n if (!isRecord(permsBlock)) return { ...EMPTY, present: true, invalid: \"`permissions` is not a mapping of subject kind to permissions\" };\n\n const byId = new Map<string, Permission>();\n for (const [subject, block] of Object.entries(permsBlock)) {\n // A permission under an undeclared subject is the typo this shape exists to\n // make impossible: `Requires: profiles/channel:send` against a `profile`\n // block resolves nowhere, and without this the vocabulary would simply grow\n // a second subject kind nobody meant to declare.\n if (!subjects.includes(subject)) {\n return { ...EMPTY, present: true, invalid: `'${subject}' holds permissions but is not declared under \\`subjects\\`` };\n }\n if (!isRecord(block)) {\n return { ...EMPTY, present: true, invalid: `\\`permissions.${subject}\\` is not a mapping of permission name to its declaration` };\n }\n for (const [name, body] of Object.entries(block)) {\n const decl = isRecord(body) ? body : {};\n const enforced = decl[\"enforced_by\"];\n byId.set(`${subject}/${name}`, {\n subject,\n name,\n id: `${subject}/${name}`,\n ...(typeof decl[\"description\"] === \"string\" ? { description: decl[\"description\"] } : {}),\n ...(typeof decl[\"owned_by\"] === \"string\" ? { ownedBy: decl[\"owned_by\"] } : {}),\n enforcedBy: Array.isArray(enforced) ? enforced.filter((s): s is string => typeof s === \"string\") : [],\n });\n }\n }\n return { present: true, subjects, byId };\n}\n"]}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The OpenAPI half of projecting a feature onto one service: which operations
3
+ * its contract delta defines, as slots rather than names.
4
+ *
5
+ * Moved out of `commands/delta/slices.ts` (rule 8 in docs/DESIGN.md) when
6
+ * `loam context` became the second caller — a command may not import another
7
+ * command, and a second copy of a projection is a second chance for the brief
8
+ * and the pack to disagree about what a feature changes. The answers here are
9
+ * what `loam delta --json` serializes verbatim, so they are computed once and
10
+ * never re-derived while printing — a printed number that disagrees with the
11
+ * payload beside it is the failure this separation prevents.
12
+ */
13
+ import { existsSync } from "node:fs";
14
+ import { readFile } from "node:fs/promises";
15
+ import { parse as parseYaml } from "yaml";
16
+ import { readOpenapi } from "../openapi/doc.js";
17
+ import { isRecord } from "../kernel/records.js";
18
+ import { compareIds } from "../repo/entries.js";
19
+ /**
20
+ * The feature's OpenAPI delta for one service, as slots rather than names.
21
+ *
22
+ * The operation set comes from core/openapi/doc.ts — the same structure-aware reader
23
+ * every other check uses, so this view cannot see an operation validate does not.
24
+ * `summary` is the one field that reader does not carry, and it is the sentence
25
+ * an implementer most wants, so it is looked up by the path+method the reader
26
+ * already resolved: a second read of the same document, never a second opinion
27
+ * about what an operation IS.
28
+ */
29
+ export async function apiChanges(openapiPath) {
30
+ if (!existsSync(openapiPath))
31
+ return { changes: [], unreadable: false };
32
+ // `readOpenapi`, not `operations`: the operation list alone cannot tell a
33
+ // contract that defines nothing from one that could not be read, and this is
34
+ // the command whose output IS the implementation task.
35
+ const api = await readOpenapi(openapiPath);
36
+ if (api.unreadable) {
37
+ return { changes: [], unreadable: true, ...(api.error === undefined ? {} : { error: api.error }) };
38
+ }
39
+ const ops = api.ops;
40
+ if (ops.length === 0)
41
+ return { changes: [], unreadable: false };
42
+ let doc;
43
+ try {
44
+ doc = parseYaml(await readFile(openapiPath, "utf8"));
45
+ }
46
+ catch {
47
+ // Unreadable is impossible here (the reader above said otherwise), but a
48
+ // summary is decoration and must never be the reason a task brief fails to
49
+ // print.
50
+ doc = null;
51
+ }
52
+ // The summary walk below descends four levels into a document nobody has
53
+ // validated, and `isRecord` is what it asks at each step: a cast there would
54
+ // assert a shape the parser never promised, and a sequence or a scalar in any of
55
+ // those slots would be indexed as a mapping.
56
+ const summaryOf = (op) => {
57
+ if (!isRecord(doc))
58
+ return null;
59
+ const paths = doc["paths"];
60
+ if (!isRecord(paths))
61
+ return null;
62
+ const item = paths[op.path];
63
+ if (!isRecord(item))
64
+ return null;
65
+ const entry = item[op.method];
66
+ if (!isRecord(entry))
67
+ return null;
68
+ const summary = entry["summary"];
69
+ return typeof summary === "string" && summary.length > 0 ? summary : null;
70
+ };
71
+ return {
72
+ changes: ops
73
+ .map((op) => ({
74
+ path: op.path,
75
+ method: op.method.toUpperCase(),
76
+ operationId: op.id,
77
+ summary: summaryOf(op),
78
+ remove: op.remove,
79
+ }))
80
+ .sort((a, b) => compareIds(a.path, b.path) || compareIds(a.method, b.method)),
81
+ unreadable: false,
82
+ };
83
+ }
84
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/core/projection/api.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The OpenAPI half of projecting a feature onto one service: which operations\n * its contract delta defines, as slots rather than names.\n *\n * Moved out of `commands/delta/slices.ts` (rule 8 in docs/DESIGN.md) when\n * `loam context` became the second caller — a command may not import another\n * command, and a second copy of a projection is a second chance for the brief\n * and the pack to disagree about what a feature changes. The answers here are\n * what `loam delta --json` serializes verbatim, so they are computed once and\n * never re-derived while printing — a printed number that disagrees with the\n * payload beside it is the failure this separation prevents.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { parse as parseYaml } from \"yaml\";\nimport { readOpenapi, type Operation } from \"../openapi/doc.js\";\nimport { isRecord } from \"../kernel/records.js\";\nimport { compareIds } from \"../repo/entries.js\";\n\n/**\n * One operation the feature's OpenAPI delta defines for this service. The\n * projection is a task brief, and \"implement createSplit\" without the path and\n * the method is not a task — it is a name to go look up in a file the reader\n * was not told about.\n */\nexport interface ApiChange {\n path: string;\n method: string;\n operationId: string;\n summary: string | null;\n /** `x-loam-remove: true` — this operation is being retired, not added. */\n remove: boolean;\n}\n\n/**\n * The contract axis of the brief: the operations, and whether the document they\n * came from could be read at all.\n *\n * The two have to travel together. A feature `openapi.yaml` that does not parse\n * yields zero operations, which is the same answer as a delta that genuinely\n * changes no endpoints — and this projection is a task brief, so \"no contract\n * work here\" over a YAML error is the vacuously-green trap the architecture\n * axis already guards against.\n */\nexport interface ApiSlice {\n changes: ApiChange[];\n unreadable: boolean;\n /** The parser's own message, when there is one to quote back. */\n error?: string;\n}\n\n/**\n * The feature's OpenAPI delta for one service, as slots rather than names.\n *\n * The operation set comes from core/openapi/doc.ts — the same structure-aware reader\n * every other check uses, so this view cannot see an operation validate does not.\n * `summary` is the one field that reader does not carry, and it is the sentence\n * an implementer most wants, so it is looked up by the path+method the reader\n * already resolved: a second read of the same document, never a second opinion\n * about what an operation IS.\n */\nexport async function apiChanges(openapiPath: string): Promise<ApiSlice> {\n if (!existsSync(openapiPath)) return { changes: [], unreadable: false };\n // `readOpenapi`, not `operations`: the operation list alone cannot tell a\n // contract that defines nothing from one that could not be read, and this is\n // the command whose output IS the implementation task.\n const api = await readOpenapi(openapiPath);\n if (api.unreadable) {\n return { changes: [], unreadable: true, ...(api.error === undefined ? {} : { error: api.error }) };\n }\n const ops = api.ops;\n if (ops.length === 0) return { changes: [], unreadable: false };\n let doc: unknown;\n try {\n doc = parseYaml(await readFile(openapiPath, \"utf8\"));\n } catch {\n // Unreadable is impossible here (the reader above said otherwise), but a\n // summary is decoration and must never be the reason a task brief fails to\n // print.\n doc = null;\n }\n // The summary walk below descends four levels into a document nobody has\n // validated, and `isRecord` is what it asks at each step: a cast there would\n // assert a shape the parser never promised, and a sequence or a scalar in any of\n // those slots would be indexed as a mapping.\n const summaryOf = (op: Operation): string | null => {\n if (!isRecord(doc)) return null;\n const paths = doc[\"paths\"];\n if (!isRecord(paths)) return null;\n const item = paths[op.path];\n if (!isRecord(item)) return null;\n const entry = item[op.method];\n if (!isRecord(entry)) return null;\n const summary = entry[\"summary\"];\n return typeof summary === \"string\" && summary.length > 0 ? summary : null;\n };\n return {\n changes: ops\n .map((op): ApiChange => ({\n path: op.path,\n method: op.method.toUpperCase(),\n operationId: op.id,\n summary: summaryOf(op),\n remove: op.remove,\n }))\n .sort((a, b) => compareIds(a.path, b.path) || compareIds(a.method, b.method)),\n unreadable: false,\n };\n}\n"]}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The C4 half of projecting a feature onto one service: which services exist,
3
+ * which the feature introduces, and what its tagged edges add around the one
4
+ * being projected onto. Moved out of `commands/delta/slices.ts` beside
5
+ * `./api.ts` and `./events.ts`; named `arch-slice.ts` rather than `arch.ts`
6
+ * because `core/c4/arch.ts` already exists and a twin name invites the wrong
7
+ * import.
8
+ */
9
+ import {} from "../c4/likec4.js";
10
+ import { elementService, serviceResolver } from "../c4/resolve/service.js";
11
+ import { DocsRepoUnavailableError } from "../repo/state.js";
12
+ import { listServices } from "../repo/repo.js";
13
+ /** Every service in the docs repo, or none when there is no docs repo to ask. */
14
+ export async function livingServices(docsDir) {
15
+ try {
16
+ return (await listServices(docsDir)).map((s) => s.id);
17
+ }
18
+ catch (err) {
19
+ // "There is no docs repo at all" is a different diagnosis with its own
20
+ // command (`loam doctor`); turning it into "unknown service" here would
21
+ // send the reader after a typo that is not there.
22
+ if (!(err instanceof DocsRepoUnavailableError))
23
+ throw err;
24
+ return [];
25
+ }
26
+ }
27
+ /** Services the feature's C4 delta introduces — its own tagged top-level elements. */
28
+ export function introducedServices(doc, featureId) {
29
+ if (doc === null || doc.errors.length > 0)
30
+ return [];
31
+ return doc.elements.filter((e) => e.tags.includes(featureId)).map(elementService);
32
+ }
33
+ /** The feature's tagged edges around one service, plus whether the service is new. */
34
+ export function archSlice(doc, service, featureId, known) {
35
+ const empty = { isNew: false, inbound: [], outbound: [], errors: [] };
36
+ if (doc === null)
37
+ return empty;
38
+ const { errors, elements, relationships } = doc;
39
+ if (errors.length > 0) {
40
+ return { ...empty, errors: errors.map((e) => (typeof e.line === "number" ? `L${e.line}: ${e.message}` : e.message)) };
41
+ }
42
+ // Which service an element stands for is the binding's call, not the title's
43
+ // — and the enumerated fleet rides in so the slice agrees with `validate`
44
+ // about an edge drawn into a modelled container. Without it, this projection
45
+ // told the provider "no inbound calls" and the consumer "you call 'api'" for
46
+ // the same `checkoutWeb -> paymentService.api` edge the validator was
47
+ // grading against payment-service — and the brief an agent implements from
48
+ // must never disagree with the gate it will be checked by.
49
+ const svcOf = serviceResolver(elements, known);
50
+ const edge = (r, other) => ({
51
+ service: other,
52
+ op: r.op ?? null,
53
+ title: r.title ?? null,
54
+ });
55
+ const featRels = relationships.filter((r) => r.tags.includes(featureId));
56
+ return {
57
+ isNew: elements.some((e) => elementService(e) === service && e.tags.includes(featureId)),
58
+ inbound: featRels.filter((r) => svcOf(r.target) === service).map((r) => edge(r, svcOf(r.source))),
59
+ outbound: featRels.filter((r) => svcOf(r.source) === service).map((r) => edge(r, svcOf(r.target))),
60
+ errors: [],
61
+ };
62
+ }
63
+ //# sourceMappingURL=arch-slice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arch-slice.js","sourceRoot":"","sources":["../../../src/core/projection/arch-slice.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The C4 half of projecting a feature onto one service: which services exist,\n * which the feature introduces, and what its tagged edges add around the one\n * being projected onto. Moved out of `commands/delta/slices.ts` beside\n * `./api.ts` and `./events.ts`; named `arch-slice.ts` rather than `arch.ts`\n * because `core/c4/arch.ts` already exists and a twin name invites the wrong\n * import.\n */\nimport { type Elem, type LoadedDoc, type Rel } from \"../c4/likec4.js\";\nimport { elementService, serviceResolver } from \"../c4/resolve/service.js\";\nimport { DocsRepoUnavailableError } from \"../repo/state.js\";\nimport { listServices } from \"../repo/repo.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/** One end of a feature edge, as seen from the projected service. */\nexport interface Edge {\n service: string;\n op: string | null;\n title: string | null;\n}\n\nexport interface ArchSlice {\n isNew: boolean;\n inbound: Edge[];\n outbound: Edge[];\n errors: string[];\n}\n\n/** Every service in the docs repo, or none when there is no docs repo to ask. */\nexport async function livingServices(docsDir: DocsDir): Promise<string[]> {\n try {\n return (await listServices(docsDir)).map((s) => s.id);\n } catch (err) {\n // \"There is no docs repo at all\" is a different diagnosis with its own\n // command (`loam doctor`); turning it into \"unknown service\" here would\n // send the reader after a typo that is not there.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return [];\n }\n}\n\n/** Services the feature's C4 delta introduces — its own tagged top-level elements. */\nexport function introducedServices(doc: LoadedDoc | null, featureId: string): string[] {\n if (doc === null || doc.errors.length > 0) return [];\n return doc.elements.filter((e: Elem) => e.tags.includes(featureId)).map(elementService);\n}\n\n/** The feature's tagged edges around one service, plus whether the service is new. */\nexport function archSlice(\n doc: LoadedDoc | null,\n service: string,\n featureId: string,\n known?: ReadonlySet<string>,\n): ArchSlice {\n const empty: ArchSlice = { isNew: false, inbound: [], outbound: [], errors: [] };\n if (doc === null) return empty;\n\n const { errors, elements, relationships } = doc;\n if (errors.length > 0) {\n return { ...empty, errors: errors.map((e) => (typeof e.line === \"number\" ? `L${e.line}: ${e.message}` : e.message)) };\n }\n\n // Which service an element stands for is the binding's call, not the title's\n // — and the enumerated fleet rides in so the slice agrees with `validate`\n // about an edge drawn into a modelled container. Without it, this projection\n // told the provider \"no inbound calls\" and the consumer \"you call 'api'\" for\n // the same `checkoutWeb -> paymentService.api` edge the validator was\n // grading against payment-service — and the brief an agent implements from\n // must never disagree with the gate it will be checked by.\n const svcOf = serviceResolver(elements, known);\n const edge = (r: Rel, other: string): Edge => ({\n service: other,\n op: r.op ?? null,\n title: r.title ?? null,\n });\n const featRels = relationships.filter((r) => r.tags.includes(featureId));\n\n return {\n isNew: elements.some((e) => elementService(e) === service && e.tags.includes(featureId)),\n inbound: featRels.filter((r) => svcOf(r.target) === service).map((r) => edge(r, svcOf(r.source))),\n outbound: featRels.filter((r) => svcOf(r.source) === service).map((r) => edge(r, svcOf(r.target))),\n errors: [],\n };\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The AsyncAPI half of projecting a feature onto one service — the event-axis
3
+ * mirror of `./api.ts`, moved out of `commands/delta/slices.ts` with it for
4
+ * the same second-caller reason.
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { readAsyncapi } from "../asyncapi/read.js";
8
+ /**
9
+ * The feature's AsyncAPI delta for one service, as slots rather than names —
10
+ * the event-axis mirror of `apiChanges`, read through `core/asyncapi/read.ts`
11
+ * so this view cannot see a message validate does not. Like the API slice, it
12
+ * lists the WHOLE document, restated living slots included: the delta is a
13
+ * complete document, and which slots are new is `loam validate`'s question.
14
+ */
15
+ export async function eventChanges(asyncapiPath) {
16
+ if (!existsSync(asyncapiPath))
17
+ return { changes: [], unreadable: false };
18
+ const doc = await readAsyncapi(asyncapiPath);
19
+ if (doc.unreadable) {
20
+ return { changes: [], unreadable: true, ...(doc.error === undefined ? {} : { error: doc.error }) };
21
+ }
22
+ const sent = new Set(doc.sent);
23
+ const received = new Set(doc.received);
24
+ return {
25
+ changes: doc.messages.map((m) => ({
26
+ slot: m.slot,
27
+ message: m.name,
28
+ direction: sent.has(m.name) ? "send" : received.has(m.name) ? "receive" : null,
29
+ remove: m.remove === true,
30
+ })),
31
+ unreadable: false,
32
+ };
33
+ }
34
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/core/projection/events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA+BnD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAoB;IACrD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACzE,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;IAC7C,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC;IACrG,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YAC9E,MAAM,EAAE,CAAC,CAAC,MAAM,KAAK,IAAI;SAC1B,CAAC,CAAC;QACH,UAAU,EAAE,KAAK;KAClB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The AsyncAPI half of projecting a feature onto one service — the event-axis\n * mirror of `./api.ts`, moved out of `commands/delta/slices.ts` with it for\n * the same second-caller reason.\n */\nimport { existsSync } from \"node:fs\";\nimport { readAsyncapi } from \"../asyncapi/read.js\";\n\n/**\n * One message declaration the feature's AsyncAPI delta carries for this\n * service. `slot` is where it is declared — `components.messages.<key>`, or an\n * inline `channels.<ck>.messages.<mk>` — the same spelling the validate\n * findings use; `message` is the join token an edge's `metadata { publishes }`\n * and a requirement's `Publishes:` line name.\n */\nexport interface EventChange {\n slot: string;\n message: string;\n /**\n * What the document's own operations do with the message: `send`, `receive`,\n * or null when no operation reaches it (declared but not wired — and always\n * null for a removal, whose marker joins nothing). A name both sent and\n * received reports `send`: the producer's side owns the contract.\n */\n direction: \"send\" | \"receive\" | null;\n /** `x-loam-remove: true` — this declaration retires the slot, not adds it. */\n remove: boolean;\n}\n\n/** The event axis of the brief. Same discipline as ApiSlice: the changes and the document's readability travel together. */\nexport interface EventSlice {\n changes: EventChange[];\n unreadable: boolean;\n /** The parser's own message, when there is one to quote back. */\n error?: string;\n}\n\n/**\n * The feature's AsyncAPI delta for one service, as slots rather than names —\n * the event-axis mirror of `apiChanges`, read through `core/asyncapi/read.ts`\n * so this view cannot see a message validate does not. Like the API slice, it\n * lists the WHOLE document, restated living slots included: the delta is a\n * complete document, and which slots are new is `loam validate`'s question.\n */\nexport async function eventChanges(asyncapiPath: string): Promise<EventSlice> {\n if (!existsSync(asyncapiPath)) return { changes: [], unreadable: false };\n const doc = await readAsyncapi(asyncapiPath);\n if (doc.unreadable) {\n return { changes: [], unreadable: true, ...(doc.error === undefined ? {} : { error: doc.error }) };\n }\n const sent = new Set(doc.sent);\n const received = new Set(doc.received);\n return {\n changes: doc.messages.map((m) => ({\n slot: m.slot,\n message: m.name,\n direction: sent.has(m.name) ? \"send\" : received.has(m.name) ? \"receive\" : null,\n remove: m.remove === true,\n })),\n unreadable: false,\n };\n}\n"]}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Provenance checks: is this artifact anchored to anything, and has anyone
3
+ * vouched for it?
4
+ *
5
+ * SCHEMA.md has declared `status` / `owner` / `sources` / `last_verified` since
6
+ * the beginning and nothing read them. They matter more now that the prose is
7
+ * written by an agent: coherence proves the corpus agrees with itself, and only
8
+ * `sources` says it has anything to do with the code.
9
+ *
10
+ * Absence and contradiction are graded differently on purpose. A missing field
11
+ * is incompleteness — a warning, something to work through. A field that says
12
+ * the wrong thing (a spec claiming to be another service, a status nobody
13
+ * defined) is a bug, and gates.
14
+ */
15
+ import { existsSync } from "node:fs";
16
+ import { readFile } from "node:fs/promises";
17
+ import { parseFrontmatter, readFrontmatter, stringField, FEATURE_STATUSES, SERVICE_STATUSES, } from "../document/frontmatter.js";
18
+ import { featurePaths, SPEC_AXES } from "../repo/paths.js";
19
+ import { locateServicePaths } from "../repo/service-target.js";
20
+ import { readVouchScope } from "./sample/scope.js";
21
+ import { sourceFindings } from "./sources.js";
22
+ import { contentDigest } from "./stamp.js";
23
+ /** Fields every artifact is expected to carry, beyond its identity and status. */
24
+ const EXPECTED = ["owner"];
25
+ export async function serviceProvenance(docsDir, service, opts = {}) {
26
+ const paths = await locateServicePaths(docsDir, service, opts.fleet);
27
+ const findings = [];
28
+ // Both requirement-carrying specs get the same pass: arch.spec.md follows
29
+ // spec.md's frontmatter conventions exactly, so the checks are one loop —
30
+ // only the label tells a reader which file moved. The pair comes from
31
+ // SPEC_AXES rather than being respelled, so a third axis is graded here the
32
+ // day it is declared instead of the day somebody notices.
33
+ for (const { path, file } of SPEC_AXES.map((axis) => ({ path: paths[axis.key], file: axis.file }))) {
34
+ if (!existsSync(path))
35
+ continue;
36
+ const raw = await readFile(path, "utf8");
37
+ const fm = parseFrontmatter(raw);
38
+ const label = `${service}: ${file}`;
39
+ findings.push(...identityFindings(fm, {
40
+ label,
41
+ idField: "service",
42
+ idValue: service,
43
+ statuses: [...SERVICE_STATUSES],
44
+ }));
45
+ // With no frontmatter at all, "it names no sources" says nothing the missing
46
+ // header did not already say — and a malformed one proves nothing either:
47
+ // `sources.absent` against a header nobody can read is the same false
48
+ // advice as the field cascade, so frontmatter.malformed stands alone.
49
+ if (fm.present && !fm.malformed) {
50
+ findings.push(...(await sourceFindings(fm, service, label, opts.repoDir)));
51
+ findings.push(...contentFindings(fm, raw, service, label));
52
+ findings.push(...scopeFindings(fm, service, label));
53
+ }
54
+ }
55
+ return findings;
56
+ }
57
+ export async function featureProvenance(featureDir, featureId) {
58
+ const path = featurePaths(featureDir).intent;
59
+ if (!existsSync(path))
60
+ return [];
61
+ const fm = await readFrontmatter(path);
62
+ return identityFindings(fm, {
63
+ label: "intent.md",
64
+ idField: "feature",
65
+ idValue: featureId,
66
+ statuses: [...FEATURE_STATUSES],
67
+ });
68
+ }
69
+ function identityFindings(fm, spec) {
70
+ const findings = [];
71
+ if (!fm.present) {
72
+ return [
73
+ {
74
+ severity: "warn",
75
+ code: "frontmatter.missing",
76
+ subject: spec.idValue,
77
+ message: `${spec.label} has no frontmatter — no owner, no status, no link to the code it describes`,
78
+ },
79
+ ];
80
+ }
81
+ // One honest error instead of the cascade: with an unreadable header the
82
+ // field checks below would grade owner/status/service "missing" and send the
83
+ // author adding fields to a block YAML refuses to parse. Nothing can be
84
+ // concluded from a header nobody can read — including that it lacks fields.
85
+ if (fm.malformed) {
86
+ return [
87
+ {
88
+ severity: "error",
89
+ code: "frontmatter.malformed",
90
+ subject: spec.idValue,
91
+ message: `${spec.label} has frontmatter that does not parse as YAML — owner, status and sources are unreadable until the header is fixed`,
92
+ },
93
+ ];
94
+ }
95
+ const id = stringField(fm, spec.idField);
96
+ if (id !== undefined && id !== spec.idValue) {
97
+ findings.push({
98
+ severity: "error",
99
+ code: "frontmatter.field-mismatch",
100
+ subject: spec.idValue,
101
+ message: `${spec.label} declares ${spec.idField}: ${id}, but it lives under ${spec.idValue}`,
102
+ });
103
+ }
104
+ const status = stringField(fm, "status");
105
+ if (status !== undefined && !spec.statuses.includes(status)) {
106
+ findings.push({
107
+ severity: "error",
108
+ code: "frontmatter.status-unknown",
109
+ subject: spec.idValue,
110
+ message: `${spec.label} has status '${status}' — expected one of ${spec.statuses.join(", ")}`,
111
+ });
112
+ }
113
+ const missing = [
114
+ ...(id === undefined ? [spec.idField] : []),
115
+ ...(status === undefined ? ["status"] : []),
116
+ ...EXPECTED.filter((f) => stringField(fm, f) === undefined),
117
+ ];
118
+ if (missing.length > 0) {
119
+ findings.push({
120
+ severity: "warn",
121
+ code: "frontmatter.field-missing",
122
+ subject: spec.idValue,
123
+ message: `${spec.label} is missing ${missing.length} frontmatter field(s)`,
124
+ details: missing,
125
+ text: { detailPrefix: "- " },
126
+ });
127
+ }
128
+ return findings;
129
+ }
130
+ /**
131
+ * The doc-side half of the freshness check. `sources_digest` says whether the
132
+ * CODE moved since a person looked, and only that service's repo can recompute
133
+ * it; `content_digest` says whether the DOCUMENT did, and needs nothing but
134
+ * the document — so this runs wherever the spec is readable, `--service` and
135
+ * `--all` alike. Without it, editing a spec after it was vouched left
136
+ * `status: verified` standing over words nobody read — forged freshness, in
137
+ * the exact agent-written-prose threat model this layer exists for.
138
+ */
139
+ function contentFindings(fm, raw, service, label) {
140
+ if (stringField(fm, "status") !== "verified")
141
+ return [];
142
+ const stamped = stringField(fm, "content_digest");
143
+ // A verified doc with no stamp predates the field. Quiet on purpose: the fix
144
+ // is re-vouching, and grading years of pre-feature vouches as suspect would
145
+ // drown the fleet in warnings nobody chose.
146
+ if (stamped === undefined)
147
+ return [];
148
+ if (contentDigest(raw) === stamped)
149
+ return [];
150
+ const since = stringField(fm, "last_verified") ?? "it was vouched";
151
+ return [
152
+ {
153
+ // A warning, not an error — the sources.stale doctrine: the doc changed
154
+ // since it was vouched, and only a person can say whether verified still
155
+ // holds of the new words.
156
+ severity: "warn",
157
+ code: "content.stale",
158
+ message: `${label} changed since ${since} — the doc moved under its vouch, and only a person can say whether 'verified' still holds. Run \`loam vouch --pack --service ${service}\` for what to re-read, then \`loam vouch --service ${service}\`.`,
159
+ },
160
+ ];
161
+ }
162
+ /**
163
+ * The other half of what a stamp can hide: a `verified` document that a person
164
+ * read only a SAMPLE of.
165
+ *
166
+ * `loam vouch --sample <n>` deliberately trades assurance for a vouch that
167
+ * actually happens, and the trade is only sound while the reduction stays
168
+ * visible. `status: verified` cannot carry it — that string is a published
169
+ * contract and the maturity rung `vouched` is derived from it — so the scope
170
+ * rides beside it in `vouch_scope`, and this is the check that makes a fleet
171
+ * able to see it: one warning per sampled spec file, with the k/n, the seed
172
+ * to recompute the sample from, and the one action that clears it.
173
+ *
174
+ * A WARNING, following the `sources.stale` / `content.stale` doctrine: the
175
+ * document is not wrong, it is incompletely read, and only a person can close
176
+ * that. Warnings never gate, which is exactly what a fleet adopting sampling
177
+ * as policy needs — and a fleet that wants to gate on it branches on the code
178
+ * in its own CI, which is what stable codes are for.
179
+ *
180
+ * Doc-side, like `content.stale`: it needs no service repo, so it fires from
181
+ * the docs repo under `--service` and `--all` alike. A scope that does not
182
+ * decode still fires, saying so — `isSampled` asks about PRESENCE, because a
183
+ * mangled field must never be the way a partial read is promoted to a full
184
+ * one.
185
+ */
186
+ function scopeFindings(fm, service, label) {
187
+ if (stringField(fm, "status") !== "verified")
188
+ return [];
189
+ const stamp = readVouchScope(fm);
190
+ if (stamp.kind === "none")
191
+ return [];
192
+ const when = stringField(fm, "last_verified");
193
+ const said = stamp.kind === "unreadable"
194
+ ? `carries a vouch_scope that does not decode (${stamp.text === null ? "its value is not text at all" : `\`${stamp.text}\``}), so it is graded as sampled — an unreadable scope must never read as a full vouch`
195
+ : `was vouched from a sample: ${stamp.scope.sections} of ${stamp.scope.of} section(s) were read${when === undefined ? "" : ` on ${when}`}, ` +
196
+ `chosen by seed ${stamp.scope.seed} — the other ${stamp.scope.of - stamp.scope.sections} were read by nobody`;
197
+ return [
198
+ {
199
+ severity: "warn",
200
+ code: "sources.sampled-vouch",
201
+ subject: service,
202
+ message: `${label} ${said}. \`status: verified\` stands over the whole document either way. Only a person can clear ` +
203
+ `it, by reading the rest and running a full \`loam vouch --service ${service}\` (no --sample); ` +
204
+ `\`loam vouch --pack --service ${service}\` lists what that sample left unread.`,
205
+ },
206
+ ];
207
+ }
208
+ //# sourceMappingURL=findings.js.map