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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,31 @@
1
+ /** The closed set. The module header says what each value instructs. */
2
+ export const ARTIFACT_STATUSES = ["missing", "blocked", "draft", "ready", "done"];
3
+ /**
4
+ * The artifact kinds a feature can owe, in the order the cycle authors them.
5
+ * `spec`, `arch-spec`, `openapi` and `asyncapi` are per-service and repeat
6
+ * once per service the feature touches; `capabilities` repeats once per
7
+ * capability document the feature's delta carries; the rest are feature-wide.
8
+ *
9
+ * `capabilities` sits where an analyst writes it — the business promise comes
10
+ * before the services that keep it, and a capability-only feature is a real and
11
+ * ordinary first commit.
12
+ */
13
+ export const ARTIFACT_IDS = [
14
+ "intent",
15
+ "delta",
16
+ "capabilities",
17
+ "spec",
18
+ "arch-spec",
19
+ "openapi",
20
+ "asyncapi",
21
+ "verification",
22
+ ];
23
+ /** Nothing is written down: no living spec at all. Every other absence is partial adoption. */
24
+ export function undocumented(s) {
25
+ return !s.has.spec;
26
+ }
27
+ /** A person stamped it — `loam vouch`'s exact postcondition, read back off the frontmatter. */
28
+ export function vouched(s) {
29
+ return s.status === "verified" && s.sources.stamped;
30
+ }
31
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/core/status/report.ts"],"names":[],"mappings":"AA8EA,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAG3F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,OAAO;IACP,cAAc;IACd,MAAM;IACN,WAAW;IACX,SAAS;IACT,UAAU;IACV,cAAc;CACN,CAAC;AAqPX,+FAA+F;AAC/F,MAAM,UAAU,YAAY,CAAC,CAAe;IAC1C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,OAAO,CAAC,CAAe;IACrC,OAAO,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;AACtD,CAAC","sourcesContent":["/**\n * Where does this work stand, and what is the next thing to do?\n *\n * Every other command answers a question you already know how to ask: validate\n * grades a target you name, delta briefs a service you name, verify checks a\n * feature you name. The one thing loam could not answer was the question an\n * agent asks FIRST — the one it has when it joins a repository halfway, or\n * comes back after losing its session. That knowledge existed, but only as\n * prose inside the slash commands (\"1. scaffold, 2. author, 3. validate\"),\n * which is to say it existed for whoever had read them recently and for nobody\n * else.\n *\n * The obvious implementation is a state file — a `.loam/state.json` recording\n * which step each feature reached. It is rejected here for the reason core/verify/\n * refuses a second mutable database: the moment a second record of the truth\n * exists, an author who edits a file by hand, a merge, a rebase or a\n * `git checkout` desynchronises it, and the state file's answer is confidently\n * wrong exactly when somebody trusts it. So `status` stores nothing and caches\n * nothing. It is a projection: every invocation re-derives the whole answer\n * from the files, and a file edited in another window is visible on the next\n * run with no invalidation step for anyone to forget.\n *\n * It also grades nothing itself. Every verdict here is somebody else's\n * function: `featureCoherence` (what `loam archive` refuses on) plus\n * `featureProvenance` and `requirementsMissingScenarios` (`loam validate\n * --feature`'s other error sources, as far as they exist as shared functions),\n * and `readVerificationState` + `featureChecklist`, the pair `loam verify`\n * uses. A second opinion about whether a feature holds together would be a\n * second answer to grade a pull request against, and the one a reader consults\n * is never the one the gate obeys (doctor.ts learnt this with its own config\n * validator). A check that lives inline inside `validate` cannot be inherited\n * at all, which is why `stage` never rests on `checks.coherent` alone.\n *\n * Which is why the rule this module is held to is one-directional: **status may\n * be more pessimistic than the gates, never greener.** `validate` and `archive`\n * answer two different questions — is the DOCUMENT valid, is the MERGE safe\n * (issue.ts) — and a projection that reported only one of them told an author\n * \"ship it\" about a feature the other one refuses. So `stage` and `next[]` take\n * the UNION: an error `validate` grades and a warning `archive` gates on both\n * make a feature `draft`, and test/status-agrees-with-gate.test.ts holds the\n * direction on a matrix of deliberately-damaged trees.\n *\n * ## The status vocabulary\n *\n * Five values, closed, and an agent is expected to branch on them:\n *\n * `missing` the artifact is owed and is not on disk; nothing stands in the\n * way of writing it now.\n * `blocked` the work is stalled on something that is NOT this artifact — an\n * artifact it is derived from is absent, or another feature in\n * flight has to archive first.\n * `draft` it is on disk and the shared checks report an error against it:\n * what exists is wrong.\n * `ready` it is on disk and clean, but its obligation is not discharged —\n * something outside the documents (code, a test run, a recording)\n * still has to answer it.\n * `done` nothing is owed here.\n *\n * The two pairs that look alike are the point of the set. `missing` and\n * `blocked` both mean \"not written\", and they are different instructions: one\n * says write it now, the other says you cannot yet and names what comes first —\n * an agent that cannot tell them apart authors a verification record against a\n * checklist that does not exist. `draft` and `ready` both mean \"on disk\", and\n * they are different instructions too: `draft` says the document is wrong and\n * the fix is in this repository, `ready` says the document is right and the\n * world has not caught up — the code is not built, or the claims are not\n * answered. Collapsing those two would send an agent to re-edit a spec that is\n * already correct.\n *\n * A value unreachable in a scope is simply never emitted there, deliberately:\n * the fleet form never says `draft`, because grading is a per-feature cost it\n * refuses to pay.\n */\nimport type { ServiceEntry } from \"../repo/entries.js\";\nimport type { UseCaseBlastRadius } from \"../usecases/touch.js\";\nimport type { VerificationVerdict } from \"../verify/record.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\n\n/** The closed set. The module header says what each value instructs. */\nexport const ARTIFACT_STATUSES = [\"missing\", \"blocked\", \"draft\", \"ready\", \"done\"] as const;\nexport type ArtifactStatus = (typeof ARTIFACT_STATUSES)[number];\n\n/**\n * The artifact kinds a feature can owe, in the order the cycle authors them.\n * `spec`, `arch-spec`, `openapi` and `asyncapi` are per-service and repeat\n * once per service the feature touches; `capabilities` repeats once per\n * capability document the feature's delta carries; the rest are feature-wide.\n *\n * `capabilities` sits where an analyst writes it — the business promise comes\n * before the services that keep it, and a capability-only feature is a real and\n * ordinary first commit.\n */\nexport const ARTIFACT_IDS = [\n \"intent\",\n \"delta\",\n \"capabilities\",\n \"spec\",\n \"arch-spec\",\n \"openapi\",\n \"asyncapi\",\n \"verification\",\n] as const;\nexport type ArtifactId = (typeof ARTIFACT_IDS)[number];\n\nexport interface ArtifactState {\n id: ArtifactId;\n /** The service this artifact belongs to, or null for the feature-wide ones. */\n service: string | null;\n /**\n * The capability this artifact belongs to, on `capabilities` rows only.\n *\n * A field of its own rather than a capability id put in `service`: the two\n * are different namespaces — a fleet may hold a service and a capability of\n * one name — and bending a published field to carry the other kind is how a\n * consumer grouping by service silently acquires a row that is not one. Absent\n * on every other row, so nothing that reads `service` today changes.\n */\n capability?: string;\n /** Repo-relative with forward slashes — diffable across machines, like every path in the contract. */\n path: string;\n exists: boolean;\n /**\n * Whether ABSENCE is a defect. False for the artifacts a feature may\n * legitimately not have: `delta.likec4` (a requirements-only change deletes\n * it), `arch.spec.md` and `asyncapi.yaml` (optional everywhere), and\n * `openapi.yaml` for a service\n * the living docs already carry. An absent optional artifact is `done` —\n * nothing is owed — and `exists: false` beside it is how a reader tells that\n * from a file that is present and fine.\n */\n required: boolean;\n status: ArtifactStatus;\n /** Artifact ids that must come first. Non-empty only when `status` is `blocked`. */\n blockedBy: string[];\n}\n\nexport interface NextStep {\n /** Stable — prose may change, this may not. */\n code: string;\n /** One sentence, actionable without reading any other field. */\n statement: string;\n /** The literal command to run. */\n command: string;\n artifact?: ArtifactId;\n service?: string;\n /** The repo-relative file the step is about, when it names one. */\n path?: string;\n}\n\nexport type NextActionKind = \"command\" | \"edit\" | \"external-repo\" | \"human-review\";\nexport type NextActionCwd = \"configured-repo\" | \"docs-repo\" | \"service-repo\";\n\n/**\n * The machine contract beside the legacy `NextStep.command` string.\n *\n * `command` exists only when every argument is concrete. An edit names the\n * file and puts the old command in `after`, because that command grades the\n * authored work rather than performing it. `needs` is data the caller must\n * obtain; it is never smuggled into a shell-looking placeholder.\n */\nexport interface NextExecution {\n kind: NextActionKind;\n runnable: boolean;\n cwd: NextActionCwd;\n command?: string;\n after?: string;\n needs?: string[];\n}\n\nexport type ExecutableNextStep = NextStep & { execution: NextExecution };\n\n/**\n * What the record beside the feature says, without asking whether anyone should\n * believe the code. `stale` is its own state and not merely \"recorded\": the\n * feature moved after the answers were written, so the record answers questions\n * that are no longer the ones being asked — which is exactly why `loam verify\n * --record` refuses to merge into it.\n */\nexport interface VerificationState {\n state: \"absent\" | \"unreadable\" | \"stale\" | \"recorded\";\n /** The day the record was written, when there is a readable one. */\n recorded: string | null;\n /**\n * `verify --json`'s own three-valued verdict, through `verificationVerdict` —\n * not a fourth opinion computed here. `attested` is the one that matters to a\n * reader of this command: complete, and resting on somebody's word about a\n * test rather than on a run.\n */\n verdict: VerificationVerdict;\n /** Every count below is `tallyRecord` — the record's `claims:` array, never its `summary:` block. */\n claims: number;\n confirmed: number;\n unconfirmed: number;\n /** Federated records only: claims nobody has answered yet. */\n unanswered: number;\n /** Of the confirmed, the scenario claims on an agent's word (`attestedClaims`). */\n attested: number;\n}\n\n/**\n * A `loam archive`/`unarchive` that was killed mid-commit, read off the intent\n * journal `.loam-commit` leaves in the docs repo.\n *\n * It belongs in a projection that grades nothing because it is not a grade: it\n * is the one repository state where every OTHER answer this command gives is\n * about files that may be half-written. `doctor` blocks on it; `status` is what\n * an agent runs first, and it reported `done` over a half-merged repo — so it\n * says so too, from the same journal, and leads its steps with the repair.\n *\n * Read, never repaired: recovery happens under the docs lock inside `archive`\n * and `unarchive`, and `status` writes nothing and takes no lock.\n */\nexport interface InterruptedCommit {\n /**\n * Which command was committing, or null when the journal cannot be read at\n * all. A sentence for a human, never a branch: version-2 journals carry any\n * writer's name (rebase, vouch, new, gherkin), so an enum here would be a\n * list this type has to chase.\n */\n command: string | null;\n feature: string | null;\n /** Who and when, so a human can place it against their own shell history. */\n host: string | null;\n pid: number | null;\n at: string | null;\n /** Docs-repo-relative paths that may hold half-written bytes. */\n files: string[];\n /** `.loam-commit` exists but does not parse — the worst case, because nothing can grade it. */\n unreadable: boolean;\n /** The command that recovers, or null when only a human can. */\n recover: string | null;\n}\n\nexport interface FeatureStatusReport {\n /**\n * A killed commit in this docs repo, or null. Present on both forms: it is a\n * fact about the repository, and every artifact row below it is a fact about\n * files that repository may have half-written.\n */\n interrupted: InterruptedCommit | null;\n feature: {\n id: string;\n dirName: string;\n path: string;\n archived: boolean;\n /** The feature's own rollup, from the same vocabulary as its artifacts. */\n stage: ArtifactStatus;\n /** Every service the feature touches, whatever `--service` narrowed the view to. */\n services: string[];\n /** Active features that must archive before this one, from the dependency index. */\n blockedBy: string[];\n };\n /** The `--service` narrowing, or null when the whole feature is in view. */\n service: string | null;\n artifacts: ArtifactState[];\n /**\n * The grade, from the same calls the two gates make: `featureCoherence`\n * (archive), plus `featureProvenance` and the scenario-coverage check — the\n * error sources `validate --feature` shares as functions. `ran` is false for\n * an archived feature: its delta is already merged into the living docs, so\n * re-grading it would compare a change against a world that has absorbed it.\n */\n checks: {\n ran: boolean;\n /**\n * No error among the findings above — `loam validate --feature`'s verdict\n * for every error source that is a shared function. It is not on its own\n * what makes a feature shippable: `stage` and `next[]` take the union with\n * archive's gating set, because a warning that gates is a warning this\n * field reports as coherent.\n */\n coherent: boolean;\n errors: number;\n warnings: number;\n /** How many findings would stop `loam archive` without `--approve`. */\n gating: number;\n /**\n * Findings, not Issues: the set is wider than coherence now, and `gates` is\n * resolved on every coherence finding so a consumer never re-implements the\n * severity default. One breach reported by `validate` and by `status` has\n * to arrive in one shape.\n */\n issues: Finding[];\n };\n verification: VerificationState;\n /**\n * The declared business flows the services IN VIEW are hops of — blast\n * radius, and the only field here that is a fact about the fleet rather than\n * about the feature.\n *\n * It narrows with `--service` exactly as `artifacts` and `next` do, because\n * `--service` is a lens on what the reader is being asked to do next. What it\n * must never do is go silently empty: `unreadable` is how it says that\n * `architecture/` could not be read, which is a different fact from a fleet\n * that draws no use cases and produces the identical empty list.\n *\n * The FLEET form has no equivalent, deliberately. That form grades nothing and\n * is coarse by design, and a per-feature flow join over every feature in flight\n * is exactly the per-feature cost it refuses to pay.\n */\n useCases: UseCaseBlastRadius;\n next: NextStep[];\n}\n\nexport interface FleetFeatureState {\n id: string;\n dirName: string;\n path: string;\n stage: ArtifactStatus;\n services: string[];\n blockedBy: string[];\n /** Artifacts the feature owes and has not written. Presence only — the fleet form grades nothing. */\n missing: string[];\n verification: VerificationState;\n}\n\nexport interface FleetStatusReport {\n /** See {@link FeatureStatusReport.interrupted}. */\n interrupted: InterruptedCommit | null;\n /**\n * Three disjoint buckets over `services/`, summing to `total`. Deliberately\n * coarser than `list`'s maturity ladder, and deliberately not the same words:\n * that ladder grades an adoption campaign rung by rung, this asks only\n * whether there is anything here to build a feature on.\n */\n services: {\n total: number;\n /** No `spec.md` — nothing about this service is written down. */\n undocumented: number;\n /** Documented, with no vouch stamp behind it. */\n draft: number;\n /** `status: verified` with a `sources_digest` — a person stood behind it. */\n vouched: number;\n /**\n * Of `vouched`, the ones read from a recorded SAMPLE rather than in full\n * (`vouch_scope`, either axis) — a SUBSET: the three above still sum to `total`.\n */\n sampledVouched: number;\n };\n features: FleetFeatureState[];\n /** Dependencies before consumers — which feature has to archive first. */\n order: string[];\n service: string | null;\n next: NextStep[];\n}\n\n/** Nothing is written down: no living spec at all. Every other absence is partial adoption. */\nexport function undocumented(s: ServiceEntry): boolean {\n return !s.has.spec;\n}\n\n/** A person stamped it — `loam vouch`'s exact postcondition, read back off the frontmatter. */\nexport function vouched(s: ServiceEntry): boolean {\n return s.status === \"verified\" && s.sources.stamped;\n}\n"]}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Reading a feature's per-service deltas without grading them. Both status
3
+ * forms draw on this one scan: it is how the artifact table and the fleet form
4
+ * decide which services owe a contract, and where the scenario steps come from.
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { requirementsMissingScenarios } from "../document/scenarios.js";
8
+ import {} from "../document/spec.js";
9
+ import { repoPath } from "../envelope/json.js";
10
+ import { featureSpecPaths, SPEC_AXES } from "../repo/paths.js";
11
+ export async function scanDeltas(docsDir, feature, services, context) {
12
+ const out = [];
13
+ for (const svc of services) {
14
+ const p = featureSpecPaths(feature.dir, svc);
15
+ // SPEC_AXES already carries the prose name of each axis — "requirements"
16
+ // and "arch requirements" are its `label`s verbatim — so the pair is read
17
+ // from there rather than spelled again, and a service's status can never
18
+ // walk a different set of spec files than its validation does.
19
+ for (const [file, label] of SPEC_AXES.map((axis) => [p[axis.key], `${svc}: ${axis.label}`])) {
20
+ if (!existsSync(file))
21
+ continue;
22
+ const reqs = await context.readRequirements(file);
23
+ out.push({
24
+ service: svc,
25
+ path: repoPath(docsDir, file),
26
+ label,
27
+ bare: requirementsMissingScenarios(reqs),
28
+ scenarios: reqs.reduce((n, r) => n + r.scenarios.length, 0),
29
+ operations: file === p.spec
30
+ ? reqs.filter((r) => r.kind !== "REMOVED").reduce((n, r) => n + r.operations.length, 0)
31
+ : 0,
32
+ });
33
+ }
34
+ }
35
+ return out;
36
+ }
37
+ /** Services this feature sends at least one operation to — see contracts.ts's `owesContract`. */
38
+ export function governedServices(scans) {
39
+ return new Set(scans.filter((s) => s.operations > 0).map((s) => s.service));
40
+ }
41
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/core/status/scan.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAoB,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAyB/D,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAgB,EAChB,OAAqB,EACrB,QAA2B,EAC3B,OAAqB;IAErB,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC7C,yEAAyE;QACzE,0EAA0E;QAC1E,yEAAyE;QACzE,+DAA+D;QAC/D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,GAAG,CACvC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC,KAAK,EAAE,CAAU,CAC1D,EAAE,CAAC;YACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,SAAS;YAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAClD,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;gBAC7B,KAAK;gBACL,IAAI,EAAE,4BAA4B,CAAC,IAAI,CAAC;gBACxC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC3D,UAAU,EACR,IAAI,KAAK,CAAC,CAAC,IAAI;oBACb,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;oBACvF,CAAC,CAAC,CAAC;aACR,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IACjD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["/**\n * Reading a feature's per-service deltas without grading them. Both status\n * forms draw on this one scan: it is how the artifact table and the fleet form\n * decide which services owe a contract, and where the scenario steps come from.\n */\nimport { existsSync } from \"node:fs\";\nimport { requirementsMissingScenarios } from \"../document/scenarios.js\";\nimport { type Requirement } from \"../document/spec.js\";\nimport { repoPath } from \"../envelope/json.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { FeatureEntry } from \"../repo/entries.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../repo/paths.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport type { PathableService } from \"../kernel/ids/service.js\";\n\n/**\n * One read of each per-service delta, answering the three questions the two\n * status forms ask of it: which requirements carry no scenario (via the same\n * `requirementsMissingScenarios` every coverage check calls — REMOVED\n * requirements are exempt there and stay exempt here, content on its way out\n * owes no acceptance criteria), how many scenarios there are to generate a\n * suite from, and whether the delta governs any operation at all — which is\n * what decides whether the service is owed a contract.\n */\nexport interface DeltaScan {\n service: string;\n /** Repo-relative, and the file the steps point at. */\n path: string;\n /** validate's own label for this axis: `<svc>: requirements` / `<svc>: arch requirements`. */\n label: string;\n bare: Requirement[];\n scenarios: number;\n /** Operations the non-REMOVED requirements govern. `spec.md` only, like coherence's own `reqOps`. */\n operations: number;\n}\n\nexport async function scanDeltas(\n docsDir: DocsDir,\n feature: FeatureEntry,\n services: PathableService[],\n context: FleetContext,\n): Promise<DeltaScan[]> {\n const out: DeltaScan[] = [];\n for (const svc of services) {\n const p = featureSpecPaths(feature.dir, svc);\n // SPEC_AXES already carries the prose name of each axis — \"requirements\"\n // and \"arch requirements\" are its `label`s verbatim — so the pair is read\n // from there rather than spelled again, and a service's status can never\n // walk a different set of spec files than its validation does.\n for (const [file, label] of SPEC_AXES.map(\n (axis) => [p[axis.key], `${svc}: ${axis.label}`] as const,\n )) {\n if (!existsSync(file)) continue;\n const reqs = await context.readRequirements(file);\n out.push({\n service: svc,\n path: repoPath(docsDir, file),\n label,\n bare: requirementsMissingScenarios(reqs),\n scenarios: reqs.reduce((n, r) => n + r.scenarios.length, 0),\n operations:\n file === p.spec\n ? reqs.filter((r) => r.kind !== \"REMOVED\").reduce((n, r) => n + r.operations.length, 0)\n : 0,\n });\n }\n }\n return out;\n}\n\n/** Services this feature sends at least one operation to — see contracts.ts's `owesContract`. */\nexport function governedServices(scans: DeltaScan[]): ReadonlySet<string> {\n return new Set(scans.filter((s) => s.operations > 0).map((s) => s.service));\n}\n"]}
@@ -0,0 +1,201 @@
1
+ import { featureChecklist } from "../verify/checklist.js";
2
+ import { readVerificationState } from "../verify/file.js";
3
+ import { attestedNotice, tallyRecord, verificationVerdict } from "../verify/record.js";
4
+ /**
5
+ * Is the done-check discharged? `verificationVerdict`'s answer and nobody
6
+ * else's — status re-deriving it would be a fourth place the words "verified"
7
+ * are defined, and the day one of them changed (a scenario claim on an agent's
8
+ * word stopped counting) the reader's copy would be the one still saying yes.
9
+ * `attested` is deliberately NOT `done` here: it is complete work resting on an
10
+ * assertion, which is what `ready` has always meant — the document is right and
11
+ * the world has not caught up.
12
+ */
13
+ export function fullyVerified(v) {
14
+ return v.verdict === "verified";
15
+ }
16
+ /**
17
+ * The record's own rung. `stale` and `unreadable` are `draft` because both mean
18
+ * the file on disk is the thing to fix; unconfirmed claims are `ready` because
19
+ * the file is fine and the answer lives in the code.
20
+ *
21
+ * All four states are spelled, `recorded` included, so the `never` below fails
22
+ * the build rather than the reader: a fifth record state added to
23
+ * {@link VerificationState} would otherwise fall through into the last branch
24
+ * and be graded by `fullyVerified` alone — silently answering `ready` about a
25
+ * record whose new state nobody here has decided a rung for.
26
+ */
27
+ export function verificationStatus(v) {
28
+ if (v.state === "absent")
29
+ return "missing";
30
+ if (v.state === "unreadable" || v.state === "stale")
31
+ return "draft";
32
+ if (v.state === "recorded")
33
+ return fullyVerified(v) ? "done" : "ready";
34
+ const unreachable = v.state;
35
+ throw new Error(`verificationStatus: no rung for verification state '${String(unreachable)}'`);
36
+ }
37
+ /**
38
+ * Read the record and, only when there is one, ask whether it still answers
39
+ * this feature. The checklist is derived on demand and never speculatively: it
40
+ * loads the delta into a LikeC4 workspace, and paying that in order to print
41
+ * "no record" is the cost `list` refused for its verification column.
42
+ *
43
+ * An archived feature's record is frozen history — archive merged its
44
+ * operations into the living contract, so a re-derived checklist can only
45
+ * mismatch, and calling it stale would slander every feature that shipped.
46
+ */
47
+ export async function verificationState(docsDir, feature,
48
+ // The invocation's read index, when the caller holds one. It reaches the
49
+ // checklist derivation, whose enumerations and delta parse are memo hits
50
+ // through it — the difference between one fleet walk per invocation and one
51
+ // per feature for a caller grading several (`loam gate`'s check 3).
52
+ fleet) {
53
+ const empty = {
54
+ recorded: null,
55
+ verdict: "unverified",
56
+ claims: 0,
57
+ confirmed: 0,
58
+ unconfirmed: 0,
59
+ unanswered: 0,
60
+ attested: 0,
61
+ };
62
+ const read = await readVerificationState(feature.dir);
63
+ if (read.state === "absent")
64
+ return { state: { state: "absent", ...empty }, notice: null };
65
+ // A record whose summary contradicts its own claims is refused by the reader
66
+ // itself (`verify.record-miscounted`), so this branch reports zeros rather
67
+ // than salvaging counts from a file nobody can say which half of was edited.
68
+ // Where there ARE counts they are `tallyRecord`'s, never the summary's — the
69
+ // block used to be copied out verbatim, and a record `verify --json` calls
70
+ // unverified read here as "11 of 11 confirmed".
71
+ if (read.state === "unreadable")
72
+ return { state: { state: "unreadable", ...empty }, notice: null };
73
+ const v = read.verification;
74
+ const stale = feature.archived
75
+ ? false
76
+ : (await featureChecklist(docsDir, feature.dir, feature.id, fleet)).digest !== v.checklist;
77
+ const tally = tallyRecord(v);
78
+ return {
79
+ state: {
80
+ state: stale ? "stale" : "recorded",
81
+ recorded: v.recorded,
82
+ verdict: verificationVerdict(tally, stale),
83
+ claims: tally.claims,
84
+ confirmed: tally.confirmed,
85
+ unconfirmed: tally.unconfirmed,
86
+ unanswered: tally.unanswered,
87
+ attested: tally.attested,
88
+ },
89
+ notice: attestedNotice(v.claims, feature.id),
90
+ };
91
+ }
92
+ export function verifyStep(id, verification, view) {
93
+ const { artifacts = [], services, boundService } = view;
94
+ const record = artifacts.find((a) => a.id === "verification");
95
+ const open = verification.unconfirmed + verification.unanswered;
96
+ // The code is spelled at each call site as a `code:` property rather than
97
+ // passed positionally, and the helper takes the whole pair: test/codes-drift.ts
98
+ // collects the vocabulary by reading the source for that exact shape, so a
99
+ // factory with a `code` PARAMETER would hide every code it emits from the one
100
+ // guard that checks they are documented. Same DRY-ness, visible to the guard.
101
+ const step = (s) => [
102
+ { ...s, command: `loam verify ${id} --json`, artifact: "verification", path: record.path },
103
+ ];
104
+ if (record.status === "blocked") {
105
+ return step({
106
+ code: "next.verify",
107
+ statement: `${id} has nothing to derive a checklist from yet — write the delta and the requirement deltas first.`,
108
+ });
109
+ }
110
+ if (verification.state === "absent") {
111
+ return step({
112
+ code: "next.verify",
113
+ statement: `Nothing has been verified for ${id} — derive the checklist and answer every claim with evidence.`,
114
+ });
115
+ }
116
+ if (verification.state === "unreadable") {
117
+ return step({
118
+ code: "next.verify",
119
+ statement: `${record.path} exists but does not read as a record — repair or delete it; nothing will overwrite it while it is unreadable.`,
120
+ });
121
+ }
122
+ if (verification.state === "stale") {
123
+ return step({
124
+ code: "next.verify",
125
+ statement: `${id} moved after ${record.path} was written — the record answers a checklist that is no longer the one being asked.`,
126
+ });
127
+ }
128
+ if (open > 0) {
129
+ // The recording form only when this repository is one of the services that
130
+ // owes an answer: `verify --record --service` binds the answers to THIS
131
+ // repo's HEAD and refuses anywhere else, so handing a docs-repo reader that
132
+ // command would name a step it cannot take.
133
+ return boundService !== undefined && services.includes(boundService)
134
+ ? [
135
+ {
136
+ code: "next.attest-service",
137
+ statement: `${open} of ${verification.claims} claim(s) on ${id} are open, and this repository is '${boundService}' — ` +
138
+ `answer the ones filed under it, bound to this commit; the rest belong to their own repositories.`,
139
+ command: `loam verify ${id} --record answers.json --service ${boundService}`,
140
+ artifact: "verification",
141
+ service: boundService,
142
+ path: record.path,
143
+ },
144
+ ]
145
+ : step({
146
+ code: "next.verify-unconfirmed",
147
+ statement: `${open} of ${verification.claims} claim(s) on ${id} are not confirmed — close them from each affected service's own repository.`,
148
+ });
149
+ }
150
+ if (verification.claims === 0) {
151
+ return step({
152
+ code: "next.verify",
153
+ statement: `${record.path} answers no claims at all — nothing was asked, so nothing was checked; re-derive the checklist and see what this feature actually promises.`,
154
+ });
155
+ }
156
+ if (verification.verdict === "attested") {
157
+ return [
158
+ {
159
+ code: "next.verify-attested",
160
+ statement: `${verification.attested} of ${id}'s scenario claim(s) are confirmed on an agent's word, not on a test run ` +
161
+ `(verify.scenario-attested) — answer them from a cucumber report, and the record stops resting on an assertion.`,
162
+ command: `loam verify ${id} --results <cucumber-report.json>`,
163
+ artifact: "verification",
164
+ path: record.path,
165
+ },
166
+ ];
167
+ }
168
+ return [];
169
+ }
170
+ /**
171
+ * Where a test comes from. `loam gherkin` writes the .feature files whose green
172
+ * run is the only thing that may answer a `scenario.tested` claim, and it runs
173
+ * from the service's own repository — so the step exists wherever there are
174
+ * scenarios to generate from, and names the repository to run it in when that
175
+ * is not this one.
176
+ */
177
+ export function testStep(id, verification, view) {
178
+ const { services, scans, boundService } = view;
179
+ if (fullyVerified(verification))
180
+ return [];
181
+ const withScenarios = [
182
+ ...new Set(scans.filter((s) => s.scenarios > 0 && services.includes(s.service)).map((s) => s.service)),
183
+ ];
184
+ if (withScenarios.length === 0)
185
+ return [];
186
+ const svc = boundService !== undefined && withScenarios.includes(boundService) ? boundService : withScenarios[0];
187
+ const here = svc === boundService;
188
+ return [
189
+ {
190
+ code: "next.generate-tests",
191
+ statement: `${id}'s delta for ${svc} carries scenarios that no test run has answered yet — generate the suite, then implement against it` +
192
+ (here
193
+ ? "."
194
+ : `. \`loam gherkin\` writes into the service's own repository, so run it from ${svc}'s${withScenarios.length > 1 ? ` (and likewise for ${withScenarios.slice(1).join(", ")})` : ""}.`),
195
+ command: `loam gherkin ${id} --service ${svc}`,
196
+ artifact: "spec",
197
+ service: svc,
198
+ },
199
+ ];
200
+ }
201
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../../../src/core/status/verification.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,WAAW,EAA+C,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAKpI;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,CAAoB;IAChD,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC;AAClC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,CAAoB;IACrD,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACpE,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU;QAAE,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACvE,MAAM,WAAW,GAAU,CAAC,CAAC,KAAK,CAAC;IACnC,MAAM,IAAI,KAAK,CAAC,uDAAuD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AACjG,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAgB,EAChB,OAAqB;AACrB,yEAAyE;AACzE,yEAAyE;AACzE,4EAA4E;AAC5E,oEAAoE;AACpE,KAAoB;IAEpB,MAAM,KAAK,GAAG;QACZ,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,YAAmC;QAC5C,MAAM,EAAE,CAAC;QACT,SAAS,EAAE,CAAC;QACZ,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,CAAC;QACb,QAAQ,EAAE,CAAC;KACZ,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3F,6EAA6E;IAC7E,2EAA2E;IAC3E,6EAA6E;IAC7E,6EAA6E;IAC7E,2EAA2E;IAC3E,gDAAgD;IAChD,IAAI,IAAI,CAAC,KAAK,KAAK,YAAY;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAEnG,MAAM,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ;QAC5B,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,CAAC,MAAM,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,SAAS,CAAC;IAC7F,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO;QACL,KAAK,EAAE;YACL,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU;YACnC,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,OAAO,EAAE,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC;YAC1C,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB;QACD,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;KAC7C,CAAC;AACJ,CAAC;AAkBD,MAAM,UAAU,UAAU,CAAC,EAAU,EAAE,YAA+B,EAAE,IAAc;IACpF,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,cAAc,CAAE,CAAC;IAC/D,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,GAAG,YAAY,CAAC,UAAU,CAAC;IAChE,0EAA0E;IAC1E,gFAAgF;IAChF,2EAA2E;IAC3E,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,IAAI,GAAG,CAAC,CAAsC,EAAc,EAAE,CAAC;QACnE,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;KAC3F,CAAC;IAEF,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;YACV,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,GAAG,EAAE,iGAAiG;SAClH,CAAC,CAAC;IACL,CAAC;IACD,IAAI,YAAY,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;YACV,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,iCAAiC,EAAE,+DAA+D;SAC9G,CAAC,CAAC;IACL,CAAC;IACD,IAAI,YAAY,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;YACV,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,GAAG,MAAM,CAAC,IAAI,gHAAgH;SAC1I,CAAC,CAAC;IACL,CAAC;IACD,IAAI,YAAY,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;YACV,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,GAAG,EAAE,gBAAgB,MAAM,CAAC,IAAI,sFAAsF;SAClI,CAAC,CAAC;IACL,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,2EAA2E;QAC3E,wEAAwE;QACxE,4EAA4E;QAC5E,4CAA4C;QAC5C,OAAO,YAAY,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;YAClE,CAAC,CAAC;gBACE;oBACE,IAAI,EAAE,qBAAqB;oBAC3B,SAAS,EACP,GAAG,IAAI,OAAO,YAAY,CAAC,MAAM,gBAAgB,EAAE,sCAAsC,YAAY,MAAM;wBAC3G,kGAAkG;oBACpG,OAAO,EAAE,eAAe,EAAE,oCAAoC,YAAY,EAAE;oBAC5E,QAAQ,EAAE,cAAc;oBACxB,OAAO,EAAE,YAAY;oBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;iBAClB;aACF;YACH,CAAC,CAAC,IAAI,CAAC;gBACH,IAAI,EAAE,yBAAyB;gBAC/B,SAAS,EAAE,GAAG,IAAI,OAAO,YAAY,CAAC,MAAM,gBAAgB,EAAE,8EAA8E;aAC7I,CAAC,CAAC;IACT,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;YACV,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,GAAG,MAAM,CAAC,IAAI,6IAA6I;SACvK,CAAC,CAAC;IACL,CAAC;IACD,IAAI,YAAY,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACxC,OAAO;YACL;gBACE,IAAI,EAAE,sBAAsB;gBAC5B,SAAS,EACP,GAAG,YAAY,CAAC,QAAQ,OAAO,EAAE,2EAA2E;oBAC5G,gHAAgH;gBAClH,OAAO,EAAE,eAAe,EAAE,mCAAmC;gBAC7D,QAAQ,EAAE,cAAc;gBACxB,IAAI,EAAE,MAAM,CAAC,IAAI;aAClB;SACF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CACtB,EAAU,EACV,YAA+B,EAC/B,IAAkF;IAElF,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC/C,IAAI,aAAa,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3C,MAAM,aAAa,GAAG;QACpB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;KACvG,CAAC;IACF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,YAAY,KAAK,SAAS,IAAI,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAE,CAAC;IAClH,MAAM,IAAI,GAAG,GAAG,KAAK,YAAY,CAAC;IAClC,OAAO;QACL;YACE,IAAI,EAAE,qBAAqB;YAC3B,SAAS,EACP,GAAG,EAAE,gBAAgB,GAAG,sGAAsG;gBAC9H,CAAC,IAAI;oBACH,CAAC,CAAC,GAAG;oBACL,CAAC,CAAC,+EAA+E,GAAG,KAChF,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAC1F,GAAG,CAAC;YACV,OAAO,EAAE,gBAAgB,EAAE,cAAc,GAAG,EAAE;YAC9C,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,GAAG;SACb;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The verification record as `status` reads it: the rung the record stands on,\n * the projection of what it says, and the two `next[]` steps that discharge it\n * — where a test comes from, and what `loam verify` still wants answered.\n * Every verdict here is `core/verify/`'s own, re-read; nothing in this module is\n * a second definition of \"verified\".\n */\nimport type { FeatureEntry } from \"../repo/entries.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { featureChecklist } from \"../verify/checklist.js\";\nimport { readVerificationState } from \"../verify/file.js\";\nimport { attestedNotice, tallyRecord, type VerificationVerdict, type VerifyNotice, verificationVerdict } from \"../verify/record.js\";\nimport type { ArtifactState, ArtifactStatus, NextStep, VerificationState } from \"./report.js\";\nimport type { DeltaScan } from \"./scan.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * Is the done-check discharged? `verificationVerdict`'s answer and nobody\n * else's — status re-deriving it would be a fourth place the words \"verified\"\n * are defined, and the day one of them changed (a scenario claim on an agent's\n * word stopped counting) the reader's copy would be the one still saying yes.\n * `attested` is deliberately NOT `done` here: it is complete work resting on an\n * assertion, which is what `ready` has always meant — the document is right and\n * the world has not caught up.\n */\nexport function fullyVerified(v: VerificationState): boolean {\n return v.verdict === \"verified\";\n}\n\n/**\n * The record's own rung. `stale` and `unreadable` are `draft` because both mean\n * the file on disk is the thing to fix; unconfirmed claims are `ready` because\n * the file is fine and the answer lives in the code.\n *\n * All four states are spelled, `recorded` included, so the `never` below fails\n * the build rather than the reader: a fifth record state added to\n * {@link VerificationState} would otherwise fall through into the last branch\n * and be graded by `fullyVerified` alone — silently answering `ready` about a\n * record whose new state nobody here has decided a rung for.\n */\nexport function verificationStatus(v: VerificationState): ArtifactStatus {\n if (v.state === \"absent\") return \"missing\";\n if (v.state === \"unreadable\" || v.state === \"stale\") return \"draft\";\n if (v.state === \"recorded\") return fullyVerified(v) ? \"done\" : \"ready\";\n const unreachable: never = v.state;\n throw new Error(`verificationStatus: no rung for verification state '${String(unreachable)}'`);\n}\n\n/**\n * Read the record and, only when there is one, ask whether it still answers\n * this feature. The checklist is derived on demand and never speculatively: it\n * loads the delta into a LikeC4 workspace, and paying that in order to print\n * \"no record\" is the cost `list` refused for its verification column.\n *\n * An archived feature's record is frozen history — archive merged its\n * operations into the living contract, so a re-derived checklist can only\n * mismatch, and calling it stale would slander every feature that shipped.\n */\nexport async function verificationState(\n docsDir: DocsDir,\n feature: FeatureEntry,\n // The invocation's read index, when the caller holds one. It reaches the\n // checklist derivation, whose enumerations and delta parse are memo hits\n // through it — the difference between one fleet walk per invocation and one\n // per feature for a caller grading several (`loam gate`'s check 3).\n fleet?: FleetContext,\n): Promise<{ state: VerificationState; notice: VerifyNotice | null }> {\n const empty = {\n recorded: null,\n verdict: \"unverified\" as VerificationVerdict,\n claims: 0,\n confirmed: 0,\n unconfirmed: 0,\n unanswered: 0,\n attested: 0,\n };\n const read = await readVerificationState(feature.dir);\n if (read.state === \"absent\") return { state: { state: \"absent\", ...empty }, notice: null };\n // A record whose summary contradicts its own claims is refused by the reader\n // itself (`verify.record-miscounted`), so this branch reports zeros rather\n // than salvaging counts from a file nobody can say which half of was edited.\n // Where there ARE counts they are `tallyRecord`'s, never the summary's — the\n // block used to be copied out verbatim, and a record `verify --json` calls\n // unverified read here as \"11 of 11 confirmed\".\n if (read.state === \"unreadable\") return { state: { state: \"unreadable\", ...empty }, notice: null };\n\n const v = read.verification;\n const stale = feature.archived\n ? false\n : (await featureChecklist(docsDir, feature.dir, feature.id, fleet)).digest !== v.checklist;\n const tally = tallyRecord(v);\n return {\n state: {\n state: stale ? \"stale\" : \"recorded\",\n recorded: v.recorded,\n verdict: verificationVerdict(tally, stale),\n claims: tally.claims,\n confirmed: tally.confirmed,\n unconfirmed: tally.unconfirmed,\n unanswered: tally.unanswered,\n attested: tally.attested,\n },\n notice: attestedNotice(v.claims, feature.id),\n };\n}\n\n/**\n * The done-check step, or none. Four different situations reach `loam verify`\n * and they are not the same work — nothing recorded, a file that will not read,\n * a record the feature has moved out from under, and claims still open — so the\n * statement says which. The code splits only where the WORK splits: starting a\n * verification and finishing one are different jobs, and an agent that treats a\n * stale record as a finished one ships a feature nobody checked.\n */\n/** The narrowed view a step is written for: what to name, and to whom. */\nexport interface StepView {\n artifacts?: ArtifactState[];\n services: string[];\n /** The service this repository is bound to, when a command is standing in one. */\n boundService: string | undefined;\n}\n\nexport function verifyStep(id: string, verification: VerificationState, view: StepView): NextStep[] {\n const { artifacts = [], services, boundService } = view;\n const record = artifacts.find((a) => a.id === \"verification\")!;\n const open = verification.unconfirmed + verification.unanswered;\n // The code is spelled at each call site as a `code:` property rather than\n // passed positionally, and the helper takes the whole pair: test/codes-drift.ts\n // collects the vocabulary by reading the source for that exact shape, so a\n // factory with a `code` PARAMETER would hide every code it emits from the one\n // guard that checks they are documented. Same DRY-ness, visible to the guard.\n const step = (s: { code: string; statement: string }): NextStep[] => [\n { ...s, command: `loam verify ${id} --json`, artifact: \"verification\", path: record.path },\n ];\n\n if (record.status === \"blocked\") {\n return step({\n code: \"next.verify\",\n statement: `${id} has nothing to derive a checklist from yet — write the delta and the requirement deltas first.`,\n });\n }\n if (verification.state === \"absent\") {\n return step({\n code: \"next.verify\",\n statement: `Nothing has been verified for ${id} — derive the checklist and answer every claim with evidence.`,\n });\n }\n if (verification.state === \"unreadable\") {\n return step({\n code: \"next.verify\",\n statement: `${record.path} exists but does not read as a record — repair or delete it; nothing will overwrite it while it is unreadable.`,\n });\n }\n if (verification.state === \"stale\") {\n return step({\n code: \"next.verify\",\n statement: `${id} moved after ${record.path} was written — the record answers a checklist that is no longer the one being asked.`,\n });\n }\n if (open > 0) {\n // The recording form only when this repository is one of the services that\n // owes an answer: `verify --record --service` binds the answers to THIS\n // repo's HEAD and refuses anywhere else, so handing a docs-repo reader that\n // command would name a step it cannot take.\n return boundService !== undefined && services.includes(boundService)\n ? [\n {\n code: \"next.attest-service\",\n statement:\n `${open} of ${verification.claims} claim(s) on ${id} are open, and this repository is '${boundService}' — ` +\n `answer the ones filed under it, bound to this commit; the rest belong to their own repositories.`,\n command: `loam verify ${id} --record answers.json --service ${boundService}`,\n artifact: \"verification\",\n service: boundService,\n path: record.path,\n },\n ]\n : step({\n code: \"next.verify-unconfirmed\",\n statement: `${open} of ${verification.claims} claim(s) on ${id} are not confirmed — close them from each affected service's own repository.`,\n });\n }\n if (verification.claims === 0) {\n return step({\n code: \"next.verify\",\n statement: `${record.path} answers no claims at all — nothing was asked, so nothing was checked; re-derive the checklist and see what this feature actually promises.`,\n });\n }\n if (verification.verdict === \"attested\") {\n return [\n {\n code: \"next.verify-attested\",\n statement:\n `${verification.attested} of ${id}'s scenario claim(s) are confirmed on an agent's word, not on a test run ` +\n `(verify.scenario-attested) — answer them from a cucumber report, and the record stops resting on an assertion.`,\n command: `loam verify ${id} --results <cucumber-report.json>`,\n artifact: \"verification\",\n path: record.path,\n },\n ];\n }\n return [];\n}\n\n/**\n * Where a test comes from. `loam gherkin` writes the .feature files whose green\n * run is the only thing that may answer a `scenario.tested` claim, and it runs\n * from the service's own repository — so the step exists wherever there are\n * scenarios to generate from, and names the repository to run it in when that\n * is not this one.\n */\nexport function testStep(\n id: string,\n verification: VerificationState,\n view: { services: string[]; scans: DeltaScan[]; boundService: string | undefined },\n): NextStep[] {\n const { services, scans, boundService } = view;\n if (fullyVerified(verification)) return [];\n const withScenarios = [\n ...new Set(scans.filter((s) => s.scenarios > 0 && services.includes(s.service)).map((s) => s.service)),\n ];\n if (withScenarios.length === 0) return [];\n const svc = boundService !== undefined && withScenarios.includes(boundService) ? boundService : withScenarios[0]!;\n const here = svc === boundService;\n return [\n {\n code: \"next.generate-tests\",\n statement:\n `${id}'s delta for ${svc} carries scenarios that no test run has answered yet — generate the suite, then implement against it` +\n (here\n ? \".\"\n : `. \\`loam gherkin\\` writes into the service's own repository, so run it from ${svc}'s${\n withScenarios.length > 1 ? ` (and likewise for ${withScenarios.slice(1).join(\", \")})` : \"\"\n }.`),\n command: `loam gherkin ${id} --service ${svc}`,\n artifact: \"spec\",\n service: svc,\n },\n ];\n}\n"]}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * The capability half of the use-case axis: which flows CLAIM a declared
3
+ * capability, and which services those flows run through.
4
+ *
5
+ * `core/capabilities/usecase-join.ts` owns the tag grammar and the resolution
6
+ * ladder — it answers "what does this tag name?", including the `many` arm the
7
+ * slug's non-injectivity forces. This module asks the question the other way
8
+ * round: given a capability id somebody already has, which views carry its tag?
9
+ * That direction needs no ladder, because the id is not in doubt; it needs only
10
+ * the slug, so it takes the slug and stops.
11
+ *
12
+ * ## What this deliberately does NOT do
13
+ *
14
+ * It does not grade the tag. A view tagged `#cap-payments-refunds` matches BOTH
15
+ * `payments/refunds` and `payments-refunds` when a fleet declares both, and this
16
+ * module reports it under both — because that is what the tag says, and the tag
17
+ * is what the author wrote. `usecase.capability-unresolved` is already an ERROR
18
+ * over exactly that collision (`validate`'s use-case package), so a second
19
+ * verdict here would be a second answer to a question the gate has already
20
+ * settled. The same stance `PackPermission` takes on an undeclared permission:
21
+ * carry the claim, let validate convict it.
22
+ */
23
+ import { tagSlug, CAP_TAG_PREFIX, resolveCapabilityTags, resolveRequirementTags, } from "../capabilities/usecase-join.js";
24
+ import { capabilityRequirementIndex } from "../capabilities/findings.js";
25
+ import { compareIds } from "../repo/entries.js";
26
+ import { readUseCases } from "./fleet.js";
27
+ import { viewFile } from "./place.js";
28
+ /** The tag spelling a declared capability id would carry — `identity/tokens` → `cap-identity-tokens`. */
29
+ function tagFor(capability) {
30
+ return `${CAP_TAG_PREFIX}${tagSlug(capability)}`;
31
+ }
32
+ /** Does this view claim the capability? Exact tag match, case included, as `resolveCapabilityTags` matches. */
33
+ function claims(view, tag) {
34
+ return view.tags.includes(tag);
35
+ }
36
+ /**
37
+ * The flows claiming one declared capability, sorted by (file, id).
38
+ *
39
+ * Sorted rather than reported in LikeC4's record order for the reason every
40
+ * other list in this axis is: nothing has measured that the parse preserves
41
+ * declaration order, and a payload ordered by an unmeasured upstream detail
42
+ * reorders under a dependency bump.
43
+ */
44
+ export function flowsClaiming(scan, capability) {
45
+ if (scan.kind !== "read")
46
+ return [];
47
+ const tag = tagFor(capability);
48
+ return scan.views
49
+ .filter((view) => claims(view, tag))
50
+ .map((view) => ({ id: view.id, ...(view.title === undefined ? {} : { title: view.title }), file: viewFile(view) }))
51
+ .sort((a, b) => compareIds(a.file, b.file) || compareIds(a.id, b.id));
52
+ }
53
+ /**
54
+ * Every ENUMERATED service the flows claiming these capabilities run through.
55
+ *
56
+ * The restriction to the enumerated fleet is this function's own contract, not
57
+ * a guard somebody downstream is relying on: a step's endpoints resolve to
58
+ * whatever the element stands for, and a sequence diagram is full of endpoints
59
+ * that own no directory — the actor who starts the flow, the topic in the
60
+ * middle, the database at the end. A function called `servicesInFlows…` that
61
+ * answered `Customer` would be lying in its own name. `loam explore`, today's
62
+ * only caller, filters the result against its enumeration again for a different
63
+ * reason (it needs `ServiceEntry`s, in enumeration order), so removing the
64
+ * filter here would not currently change what that command prints — which is
65
+ * exactly why it is written down that the reason it exists is the return type,
66
+ * not the call site.
67
+ *
68
+ * The cost of the restriction, stated so the next reader can weigh it: a flow
69
+ * drawn through a service the fleet has not adopted yet contributes no seed.
70
+ * That service has no documentation to explore, so what is lost is a name in a
71
+ * list, and `usecase.step-unlinked` / `landscape.service-undocumented` are where
72
+ * the fleet is told about it.
73
+ */
74
+ export function servicesInFlowsClaiming(scan, capabilities) {
75
+ if (scan.kind !== "read")
76
+ return [];
77
+ const tags = new Set(capabilities.map(tagFor));
78
+ const known = scan.model.known;
79
+ const out = new Set();
80
+ for (const view of scan.views) {
81
+ if (![...tags].some((tag) => claims(view, tag)))
82
+ continue;
83
+ for (const step of view.steps) {
84
+ for (const id of [step.source, step.target]) {
85
+ const service = scan.resolve(id);
86
+ if (known.has(service))
87
+ out.add(service);
88
+ }
89
+ }
90
+ }
91
+ return [...out].sort(compareIds);
92
+ }
93
+ /**
94
+ * The `<capability>#<Requirement-ID>` pairs a fleet's flows RESOLVE to — the
95
+ * business promises a use case claims to keep.
96
+ *
97
+ * Lives in core rather than beside the grade that reports a broken tag, because
98
+ * two very different callers need the same answer and they must not compute it
99
+ * twice: `validate --all` subtracts these from
100
+ * `capability.requirement-unrealized` (a promise a flow keeps is not unkept),
101
+ * and `loam list capabilities` reports them beside the service requirements
102
+ * that realize the same promise. Two implementations of "which promises does
103
+ * this fleet keep" is two answers to the question the axis exists to settle.
104
+ *
105
+ * A MAP RATHER THAN A SET, because the two callers need different depths of the
106
+ * same answer and one of them needs names. `validate` only asks whether a
107
+ * promise is kept (`.has`, which a Map answers identically); a listing that said
108
+ * "kept" without saying by WHAT would leave a reader unable to open the flow, go
109
+ * look at its hops and judge whether the promise really is kept — which is the
110
+ * whole reason the listing carries the realizing service requirements by name
111
+ * rather than as a count. Widening the return type was cheaper than a second
112
+ * function, and a second function is how the two corpora start disagreeing.
113
+ *
114
+ * ONLY RESOLVED CLAIMS COUNT, and both halves must resolve: the `#cap-` tag to
115
+ * exactly one declared capability, and the `#req-` tag to exactly one of its
116
+ * requirements. A broken tag of either kind is already an ERROR
117
+ * (`usecase.capability-unresolved`, `usecase.requirement-unresolved`) and must
118
+ * never also mark a promise kept — a typo that silenced the unrealized warning
119
+ * would turn a mistake into a green fleet.
120
+ *
121
+ * `declared` carries the vocabulary ladder: `null` means there is nothing to
122
+ * grade against, and the honest answer is then an empty map — no flow can be
123
+ * said to keep a promise loam cannot see declared.
124
+ *
125
+ * Diff-stable by construction: the view ids under each promise are DEDUPLICATED
126
+ * and sorted with compareIds, so nothing here depends on LikeC4's view order,
127
+ * on how many tags one view carried, or on the readdir order the project was
128
+ * staged in. The map's own key order reaches no output — both callers look
129
+ * promises up by key — but the values are printed, so they are sorted.
130
+ */
131
+ export function useCaseRequirementClaims(views, declared, requirementsOf) {
132
+ const claimed = new Map();
133
+ if (declared !== null) {
134
+ for (const view of views) {
135
+ const scope = resolveCapabilityTags(view.tags, declared).flatMap((claim) => claim.kind === "resolved" ? [claim.id] : []);
136
+ for (const claim of resolveRequirementTags(view.tags, scope, requirementsOf)) {
137
+ if (claim.kind !== "resolved")
138
+ continue;
139
+ const key = `${claim.capability}#${claim.id}`;
140
+ const flows = claimed.get(key) ?? new Set();
141
+ flows.add(view.id);
142
+ claimed.set(key, flows);
143
+ }
144
+ }
145
+ }
146
+ return new Map([...claimed].map(([key, flows]) => [key, [...flows].sort(compareIds)]));
147
+ }
148
+ export async function promisesKeptByFlows(req) {
149
+ const scan = await readUseCases({ docsDir: req.docsDir, known: req.known });
150
+ if (scan.kind !== "read")
151
+ return { kind: "unreadable", errors: scan.errors };
152
+ // No flow, no claim — and no capability document is opened to prove it.
153
+ // `readUseCases`' byte gate answers a fleet with no use cases without starting
154
+ // LikeC4 at all, and reading every `capabilities/<id>/spec.md` here to resolve
155
+ // a tag set that is empty would put back the cost the gate exists to remove.
156
+ // The `read` arm is still what travels: the fleet WAS looked at.
157
+ if (scan.views.length === 0)
158
+ return { kind: "read", kept: new Map() };
159
+ const index = await capabilityRequirementIndex(req.vocab, req.read);
160
+ // `index.declared` rather than a second call to `gradableCapabilityIds`: the
161
+ // index applied the ladder when it was built, and two applications of one rule
162
+ // is where the two halves of this join start disagreeing about whether the
163
+ // vocabulary is gradable at all.
164
+ return {
165
+ kind: "read",
166
+ kept: useCaseRequirementClaims(scan.views, index.declared, (c) => index.byCapability.get(c)),
167
+ };
168
+ }
169
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../src/core/usecases/capability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The capability half of the use-case axis: which flows CLAIM a declared\n * capability, and which services those flows run through.\n *\n * `core/capabilities/usecase-join.ts` owns the tag grammar and the resolution\n * ladder — it answers \"what does this tag name?\", including the `many` arm the\n * slug's non-injectivity forces. This module asks the question the other way\n * round: given a capability id somebody already has, which views carry its tag?\n * That direction needs no ladder, because the id is not in doubt; it needs only\n * the slug, so it takes the slug and stops.\n *\n * ## What this deliberately does NOT do\n *\n * It does not grade the tag. A view tagged `#cap-payments-refunds` matches BOTH\n * `payments/refunds` and `payments-refunds` when a fleet declares both, and this\n * module reports it under both — because that is what the tag says, and the tag\n * is what the author wrote. `usecase.capability-unresolved` is already an ERROR\n * over exactly that collision (`validate`'s use-case package), so a second\n * verdict here would be a second answer to a question the gate has already\n * settled. The same stance `PackPermission` takes on an undeclared permission:\n * carry the claim, let validate convict it.\n */\nimport {\n tagSlug,\n CAP_TAG_PREFIX,\n resolveCapabilityTags,\n resolveRequirementTags,\n} from \"../capabilities/usecase-join.js\";\nimport { capabilityRequirementIndex } from \"../capabilities/findings.js\";\nimport type { CapabilityVocabulary } from \"../capabilities/capabilities.js\";\nimport type { Requirement } from \"../document/spec.js\";\nimport { compareIds } from \"../repo/entries.js\";\nimport type { ParsedView } from \"../c4/parsed/dynamic-views.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport { readUseCases, type UseCaseScan } from \"./fleet.js\";\nimport { viewFile } from \"./place.js\";\n\n/** One flow claiming a capability: enough to name it and to open the file it lives in. */\nexport interface ClaimingFlow {\n id: string;\n title?: string;\n /** Repo-relative document the view is written in. */\n file: string;\n}\n\n/** The tag spelling a declared capability id would carry — `identity/tokens` → `cap-identity-tokens`. */\nfunction tagFor(capability: string): string {\n return `${CAP_TAG_PREFIX}${tagSlug(capability)}`;\n}\n\n/** Does this view claim the capability? Exact tag match, case included, as `resolveCapabilityTags` matches. */\nfunction claims(view: ParsedView, tag: string): boolean {\n return view.tags.includes(tag);\n}\n\n/**\n * The flows claiming one declared capability, sorted by (file, id).\n *\n * Sorted rather than reported in LikeC4's record order for the reason every\n * other list in this axis is: nothing has measured that the parse preserves\n * declaration order, and a payload ordered by an unmeasured upstream detail\n * reorders under a dependency bump.\n */\nexport function flowsClaiming(scan: UseCaseScan, capability: string): ClaimingFlow[] {\n if (scan.kind !== \"read\") return [];\n const tag = tagFor(capability);\n return scan.views\n .filter((view) => claims(view, tag))\n .map((view) => ({ id: view.id, ...(view.title === undefined ? {} : { title: view.title }), file: viewFile(view) }))\n .sort((a, b) => compareIds(a.file, b.file) || compareIds(a.id, b.id));\n}\n\n/**\n * Every ENUMERATED service the flows claiming these capabilities run through.\n *\n * The restriction to the enumerated fleet is this function's own contract, not\n * a guard somebody downstream is relying on: a step's endpoints resolve to\n * whatever the element stands for, and a sequence diagram is full of endpoints\n * that own no directory — the actor who starts the flow, the topic in the\n * middle, the database at the end. A function called `servicesInFlows…` that\n * answered `Customer` would be lying in its own name. `loam explore`, today's\n * only caller, filters the result against its enumeration again for a different\n * reason (it needs `ServiceEntry`s, in enumeration order), so removing the\n * filter here would not currently change what that command prints — which is\n * exactly why it is written down that the reason it exists is the return type,\n * not the call site.\n *\n * The cost of the restriction, stated so the next reader can weigh it: a flow\n * drawn through a service the fleet has not adopted yet contributes no seed.\n * That service has no documentation to explore, so what is lost is a name in a\n * list, and `usecase.step-unlinked` / `landscape.service-undocumented` are where\n * the fleet is told about it.\n */\nexport function servicesInFlowsClaiming(scan: UseCaseScan, capabilities: readonly string[]): string[] {\n if (scan.kind !== \"read\") return [];\n const tags = new Set(capabilities.map(tagFor));\n const known = scan.model.known;\n const out = new Set<string>();\n for (const view of scan.views) {\n if (![...tags].some((tag) => claims(view, tag))) continue;\n for (const step of view.steps) {\n for (const id of [step.source, step.target]) {\n const service = scan.resolve(id);\n if (known.has(service)) out.add(service);\n }\n }\n }\n return [...out].sort(compareIds);\n}\n\n/**\n * The `<capability>#<Requirement-ID>` pairs a fleet's flows RESOLVE to — the\n * business promises a use case claims to keep.\n *\n * Lives in core rather than beside the grade that reports a broken tag, because\n * two very different callers need the same answer and they must not compute it\n * twice: `validate --all` subtracts these from\n * `capability.requirement-unrealized` (a promise a flow keeps is not unkept),\n * and `loam list capabilities` reports them beside the service requirements\n * that realize the same promise. Two implementations of \"which promises does\n * this fleet keep\" is two answers to the question the axis exists to settle.\n *\n * A MAP RATHER THAN A SET, because the two callers need different depths of the\n * same answer and one of them needs names. `validate` only asks whether a\n * promise is kept (`.has`, which a Map answers identically); a listing that said\n * \"kept\" without saying by WHAT would leave a reader unable to open the flow, go\n * look at its hops and judge whether the promise really is kept — which is the\n * whole reason the listing carries the realizing service requirements by name\n * rather than as a count. Widening the return type was cheaper than a second\n * function, and a second function is how the two corpora start disagreeing.\n *\n * ONLY RESOLVED CLAIMS COUNT, and both halves must resolve: the `#cap-` tag to\n * exactly one declared capability, and the `#req-` tag to exactly one of its\n * requirements. A broken tag of either kind is already an ERROR\n * (`usecase.capability-unresolved`, `usecase.requirement-unresolved`) and must\n * never also mark a promise kept — a typo that silenced the unrealized warning\n * would turn a mistake into a green fleet.\n *\n * `declared` carries the vocabulary ladder: `null` means there is nothing to\n * grade against, and the honest answer is then an empty map — no flow can be\n * said to keep a promise loam cannot see declared.\n *\n * Diff-stable by construction: the view ids under each promise are DEDUPLICATED\n * and sorted with compareIds, so nothing here depends on LikeC4's view order,\n * on how many tags one view carried, or on the readdir order the project was\n * staged in. The map's own key order reaches no output — both callers look\n * promises up by key — but the values are printed, so they are sorted.\n */\nexport function useCaseRequirementClaims(\n views: readonly ParsedView[],\n declared: readonly string[] | null,\n requirementsOf: (capability: string) => ReadonlySet<string> | undefined,\n): ReadonlyMap<string, string[]> {\n const claimed = new Map<string, Set<string>>();\n if (declared !== null) {\n for (const view of views) {\n const scope = resolveCapabilityTags(view.tags, declared).flatMap((claim) =>\n claim.kind === \"resolved\" ? [claim.id] : [],\n );\n for (const claim of resolveRequirementTags(view.tags, scope, requirementsOf)) {\n if (claim.kind !== \"resolved\") continue;\n const key = `${claim.capability}#${claim.id}`;\n const flows = claimed.get(key) ?? new Set<string>();\n flows.add(view.id);\n claimed.set(key, flows);\n }\n }\n }\n return new Map([...claimed].map(([key, flows]) => [key, [...flows].sort(compareIds)]));\n}\n\n/**\n * The same answer as `useCaseRequirementClaims`, read from disk — or the honest\n * refusal to give one.\n *\n * `validate --all` already holds the parsed project and has already applied the\n * vocabulary ladder by the time it grades, so it calls the pure function above\n * with what it has. `loam list capabilities` holds neither, and the three steps\n * between a docsDir and that answer — read the flows, take the ladder, index the\n * documents' requirement ids — are three chances to get the suppression rules\n * wrong. They live here rather than in the command for that reason and not for\n * brevity: a command that spelled `[...vocab.byId.keys()]` in place of the\n * ladder would report a flow's promise as kept against a vocabulary nobody could\n * read.\n *\n * THE `unreadable` ARM MUST TRAVEL. An `architecture/` that did not parse yields\n * no views, and an empty map is indistinguishable from \"no flow keeps\n * anything\" — which is the single wrong answer this axis exists to prevent, and\n * the reason `readUseCases` is a tagged union rather than a list.\n */\nexport type PromisesKept =\n | { kind: \"read\"; kept: ReadonlyMap<string, readonly string[]> }\n | { kind: \"unreadable\"; errors: string[] };\n\nexport interface PromisesKeptRequest {\n docsDir: DocsDir;\n vocab: CapabilityVocabulary;\n /** The enumerated fleet — `readUseCases`' own requirement, for its own reason. */\n known: ReadonlySet<string>;\n /** The requirement reader — pass a FleetContext's readRequirements, bound. */\n read: (path: string) => Promise<Requirement[]>;\n}\n\nexport async function promisesKeptByFlows(req: PromisesKeptRequest): Promise<PromisesKept> {\n const scan = await readUseCases({ docsDir: req.docsDir, known: req.known });\n if (scan.kind !== \"read\") return { kind: \"unreadable\", errors: scan.errors };\n // No flow, no claim — and no capability document is opened to prove it.\n // `readUseCases`' byte gate answers a fleet with no use cases without starting\n // LikeC4 at all, and reading every `capabilities/<id>/spec.md` here to resolve\n // a tag set that is empty would put back the cost the gate exists to remove.\n // The `read` arm is still what travels: the fleet WAS looked at.\n if (scan.views.length === 0) return { kind: \"read\", kept: new Map() };\n const index = await capabilityRequirementIndex(req.vocab, req.read);\n // `index.declared` rather than a second call to `gradableCapabilityIds`: the\n // index applied the ladder when it was built, and two applications of one rule\n // is where the two halves of this join start disagreeing about whether the\n // vocabulary is gradable at all.\n return {\n kind: \"read\",\n kept: useCaseRequirementClaims(scan.views, index.declared, (c) => index.byCapability.get(c)),\n };\n}\n"]}