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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"answers.js","sourceRoot":"","sources":["../../../src/core/verify/answers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The answers, and the one thing loam checks about them.\n *\n * loam does not judge an answer. It cannot: it never read the code. What it\n * guarantees is that every question was asked and that every answer sits next\n * to the question it answers — so an answer set that does not correspond to the\n * current checklist is refused rather than merged. An unchecked claim must\n * never be able to masquerade as checked, and that refusal is `checkAnswers`.\n *\n * A scenario claim is answered mechanically wherever it can be: `--results`\n * reads a cucumber JSON report and matches it by the digest tag `loam gherkin`\n * stamped, and only a green run confirms one (`core/results.ts`). Where it\n * cannot — a legacy service whose suite is months away, which is the fleet loam\n * exists for — `--record` may still take an agent's word for it, and\n * `./record.ts` marks that answer attested rather than run.\n */\nimport { isRecord } from \"../kernel/records.js\";\nimport { type Claim } from \"./checklist.js\";\nimport { type EvidencePin } from \"./pins/pin.js\";\n\n/**\n * Two verdicts, deliberately. Anything an agent cannot show evidence for is\n * unconfirmed — a third value for \"not applicable\" would absorb every claim\n * nobody wanted to answer.\n */\nexport const VERDICTS = [\"confirmed\", \"unconfirmed\"] as const;\nexport type Verdict = (typeof VERDICTS)[number];\n\n/**\n * Who answered a claim: the `runner` (a cucumber report's digest-tagged\n * scenarios, matched mechanically by `--results`), the `external-runner` (an\n * API contract-test report's operationId-matched entries, matched mechanically\n * by `--contract-results` — external because loam did not generate the suite\n * the way it generates the gherkin, so the join key is the operationId the\n * claim asserts rather than a digest loam stamped), or an `agent` (somebody's\n * word about the code, taken back by `--record`). On the record so a reviewer\n * can tell a green run from an assertion. `external-runner` never answers a\n * `scenario.tested` claim, which is what keeps the attested boundary —\n * `attestedClaims` in `./record.ts` — exactly where it was.\n */\nexport const ANSWERED_BY = [\"runner\", \"external-runner\", \"agent\"] as const;\nexport type AnsweredBy = (typeof ANSWERED_BY)[number];\n\nexport interface Answer {\n id: string;\n verdict: Verdict;\n /** Where it can be seen — `file:line`, or a report scenario for the runner. Required for `confirmed`. */\n evidence: string[];\n note?: string;\n answered_by: AnsweredBy;\n /**\n * loam's own stamp, never caller input: `validateServiceEvidence` computes\n * these from the attested commit's blobs and the record path attaches them.\n * `checkAnswers` below deliberately never reads the field, so an\n * `evidence_pins` key in an answers FILE is ignored, not honored — an agent\n * must not be able to hand loam a pin loam did not derive.\n */\n evidence_pins?: EvidencePin[];\n}\n\n/** Why an answer set was refused. Each names a different way it fails to answer. */\nexport type AnswerRefusal = \"answers-unreadable\" | \"answers-mismatch\" | \"answers-unevidenced\";\n\nexport type AnswerCheck =\n /** The answers, re-ordered to the checklist, one per claim. */\n | { ok: true; answers: Answer[] }\n | { ok: false; code: AnswerRefusal; message: string };\n\n/**\n * Accept an answer set only if it answers THIS checklist: every claim once, no\n * claim twice, nothing extra, and no \"confirmed\" that shows nothing.\n *\n * The refusals are the point of the command. If a stale or partial answer file\n * could be recorded, `verification.yaml` would say a claim was checked when\n * nobody ever asked it — and a record that can lie is worse than no record,\n * because it looks like evidence.\n *\n * `runnerOwned` is the composition rule under `--results` and\n * `--contract-results`: ids a mechanical report answers — scenario claims under\n * the first flag, api.exposes claims under the second — which an answers file\n * must therefore not touch. An entry naming one refuses with its own diagnosis\n * — the id IS on the feature's checklist, so calling it unknown would send the\n * caller hunting a staleness that is not there. `--diff-answers`\n * (commands/verify/cross/diff.ts) calls this with it UNSET on purpose: under a\n * diff both agents answer everything in scope, scenario claims included,\n * because the diff consults no report — so making scenario claims runner-only\n * even without `--results` would break that lens.\n *\n * `context` is what turns the last line from a circle into an instruction. In a\n * fleet the commonest way to \"miss\" a claim is that it is not yours: the\n * checklist spans ten repositories and this one can only answer its own. Told\n * only to \"answer the claims it lists\", an agent in the payment-service repo\n * re-runs verify, sees the same checkout-web claims it cannot speak for, and\n * either loops or invents evidence. So when every unanswered claim belongs to\n * another service, say THAT, and name the command that records them.\n */\nexport function checkAnswers(\n claims: Claim[],\n raw: unknown,\n runnerOwned?: ReadonlySet<string>,\n context?: { feature: string; service?: string },\n): AnswerCheck {\n const refuse = (code: AnswerRefusal, message: string): AnswerCheck => ({ ok: false, code, message });\n\n const list = Array.isArray(raw)\n ? raw\n : isRecord(raw) && Array.isArray(raw[\"answers\"])\n ? (raw[\"answers\"] as unknown[])\n : null;\n if (list === null) {\n return refuse(\n \"answers-unreadable\",\n \"Expected a JSON array of answers, or an object with an `answers` array: \" +\n `[{ \"id\": \"...\", \"verdict\": \"confirmed\", \"evidence\": [\"file:line\"] }]`,\n );\n }\n\n const answers: Answer[] = [];\n for (const [i, entry] of list.entries()) {\n const where = `answer ${i + 1}`;\n if (!isRecord(entry)) return refuse(\"answers-unreadable\", `${where} is not an object`);\n const id = entry[\"id\"];\n if (typeof id !== \"string\" || id.length === 0) {\n return refuse(\"answers-unreadable\", `${where} has no claim id`);\n }\n const verdict = entry[\"verdict\"];\n if (typeof verdict !== \"string\" || !(VERDICTS as readonly string[]).includes(verdict)) {\n return refuse(\n \"answers-unreadable\",\n `${where} ('${id}') has verdict ${JSON.stringify(verdict)} — expected one of ${VERDICTS.join(\", \")}`,\n );\n }\n const note = typeof entry[\"note\"] === \"string\" ? entry[\"note\"].trim() : \"\";\n answers.push({\n id,\n verdict: verdict as Verdict,\n evidence: stringList(entry[\"evidence\"]),\n ...(note.length > 0 ? { note } : {}),\n answered_by: \"agent\",\n });\n }\n\n const byId = new Map<string, Answer>();\n const twice: string[] = [];\n for (const a of answers) {\n if (byId.has(a.id)) twice.push(a.id);\n else byId.set(a.id, a);\n }\n const known = new Set(claims.map((c) => c.id));\n const strayIds = [...new Set(answers.map((a) => a.id).filter((id) => !known.has(id)))];\n const runnerHit = strayIds.filter((id) => runnerOwned?.has(id) === true);\n const unknown = strayIds.filter((id) => runnerOwned?.has(id) !== true);\n const missingClaims = claims.filter((c) => !byId.has(c.id));\n const missing = missingClaims.map((c) => c.id);\n if (runnerHit.length + unknown.length + missing.length + twice.length > 0) {\n const parts = [\n runnerHit.length > 0\n ? `${runnerHit.length} answer(s) name claim(s) a report owns under --results or --contract-results: ${runnerHit.join(\", \")} — the report answers those; take them out of the answers file`\n : \"\",\n unknown.length > 0 ? `${unknown.length} answer(s) name a claim that is not on the checklist: ${unknown.join(\", \")}` : \"\",\n missing.length > 0 ? `${missing.length} claim(s) have no answer: ${missing.join(\", \")}` : \"\",\n twice.length > 0 ? `${twice.length} claim(s) answered more than once: ${[...new Set(twice)].join(\", \")}` : \"\",\n ].filter((s) => s.length > 0);\n return refuse(\n \"answers-mismatch\",\n `These answers do not match the checklist — ${parts.join(\"; \")}. ` +\n missingAdvice(missingClaims, claims.filter((c) => byId.has(c.id)), context),\n );\n }\n\n const unevidenced = claims\n .map((c) => byId.get(c.id)!)\n .filter((a) => a.verdict === \"confirmed\" && a.evidence.length === 0)\n .map((a) => a.id);\n if (unevidenced.length > 0) {\n return refuse(\n \"answers-unevidenced\",\n `${unevidenced.length} claim(s) are confirmed with no evidence: ${unevidenced.join(\", \")}. A confirmation with nothing behind it is an assertion — give a file:line, or answer 'unconfirmed'.`,\n );\n }\n\n // Checklist order, not the order they were written in: the record reads in the\n // order the questions were asked, whatever the agent did.\n return { ok: true, answers: claims.map((c) => byId.get(c.id)!) };\n}\n\n/**\n * The last sentence of an `answers-mismatch`. \"Re-run and answer the claims it\n * lists\" is sound advice only when the caller CAN answer them. In a fleet the\n * checklist is federated — a feature's claims are filed under the service whose\n * code answers them — so the honest diagnosis for a repo that answered\n * everything it owns is \"the rest is not yours\", together with the form that\n * records the rest. Anything else sends the caller round the same loop.\n */\nfunction missingAdvice(\n missing: Claim[],\n answered: Claim[],\n context?: { feature: string; service?: string },\n): string {\n const generic = \"Re-run `loam verify` and answer the claims it lists.\";\n if (context === undefined || missing.length === 0 || answered.length === 0) return generic;\n // The signature of \"I answered mine and the rest is not mine\": the services\n // that own the unanswered claims and the services that own the answered ones\n // do not overlap. A single-service checklist with one claim forgotten fails\n // this test and keeps the generic advice, which is the right advice there —\n // that claim really is the caller's to answer.\n const owners = [...new Set(missing.map((c) => c.subject))].sort();\n const covered = new Set(answered.map((c) => c.subject));\n if (owners.some((s) => covered.has(s))) return generic;\n const form = `\\`loam verify ${context.feature} --record answers.json --service <svc>\\``;\n const mine = context.service;\n return mine === undefined\n ? `Those claims are owned by ${owners.join(\", \")} — each service attests its own from its own repository (${form}); ` +\n \"this all-at-once form has to answer the whole checklist in one file.\"\n : `Every unanswered claim belongs to a different service (${owners.join(\", \")}) and this repository can only attest '${mine}' — ` +\n `run ${form} in each of those repositories instead.`;\n}\n\nfunction stringList(v: unknown): string[] {\n if (typeof v === \"string\") return v.trim().length > 0 ? [v.trim()] : [];\n if (!Array.isArray(v)) return [];\n return v.map((x) => String(x).trim()).filter((s) => s.length > 0);\n}\n\n"]}
1
+ {"version":3,"file":"answers.js","sourceRoot":"","sources":["../../../src/core/verify/answers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The answers, and the one thing loam checks about them.\n *\n * loam does not judge an answer. It cannot: it never read the code. What it\n * guarantees is that every question was asked and that every answer sits next\n * to the question it answers — so an answer set that does not correspond to the\n * current checklist is refused rather than merged. An unchecked claim must\n * never be able to masquerade as checked, and that refusal is `checkAnswers`.\n *\n * A scenario claim is answered mechanically wherever it can be: `--results`\n * reads a cucumber JSON report and matches it by the digest tag `loam gherkin`\n * stamped, and only a green run confirms one (`core/results.ts`). Where it\n * cannot — a legacy service whose suite is months away, which is the fleet loam\n * exists for — `--record` may still take an agent's word for it, and\n * `./record.ts` marks that answer attested rather than run.\n */\nimport { isRecord } from \"../kernel/records.js\";\nimport { type Claim } from \"./checklist.js\";\nimport { type EvidencePin } from \"./pins/pin.js\";\n\n/**\n * Two verdicts, deliberately. Anything an agent cannot show evidence for is\n * unconfirmed — a third value for \"not applicable\" would absorb every claim\n * nobody wanted to answer.\n */\nexport const VERDICTS = [\"confirmed\", \"unconfirmed\"] as const;\nexport type Verdict = (typeof VERDICTS)[number];\n\n/**\n * Who answered a claim: the `runner` (a cucumber report's digest-tagged\n * scenarios, matched mechanically by `--results`), the `external-runner` (an\n * API contract-test report's operationId-matched entries, matched mechanically\n * by `--contract-results` — external because loam did not generate the suite\n * the way it generates the gherkin, so the join key is the operationId the\n * claim asserts rather than a digest loam stamped), or an `agent` (somebody's\n * word about the code, taken back by `--record`). On the record so a reviewer\n * can tell a green run from an assertion. `external-runner` never answers a\n * `scenario.tested` claim, which is what keeps the attested boundary —\n * `attestedClaims` in `./record.ts` — exactly where it was.\n */\nexport const ANSWERED_BY = [\"runner\", \"external-runner\", \"agent\"] as const;\nexport type AnsweredBy = (typeof ANSWERED_BY)[number];\n\nexport interface Answer {\n id: string;\n verdict: Verdict;\n /** Where it can be seen — `file:line`, or a report scenario for the runner. Required for `confirmed`. */\n evidence: string[];\n note?: string;\n answered_by: AnsweredBy;\n /**\n * loam's own stamp, never caller input: `validateServiceEvidence` computes\n * these from the attested commit's blobs and the record path attaches them.\n * `checkAnswers` below deliberately never reads the field, so an\n * `evidence_pins` key in an answers FILE is ignored, not honored — an agent\n * must not be able to hand loam a pin loam did not derive.\n */\n evidence_pins?: EvidencePin[];\n}\n\n/**\n * The entries an answers file holds — a bare array, or an object with an\n * `answers` array — or null for any other shape. One reading of the file's\n * shape for `checkAnswers` and `--diff-answers`' off-lens note, so the two\n * cannot disagree about which entries a file has.\n */\nexport function answerEntries(raw: unknown): unknown[] | null {\n if (Array.isArray(raw)) return raw;\n return isRecord(raw) && Array.isArray(raw[\"answers\"]) ? (raw[\"answers\"] as unknown[]) : null;\n}\n\n/** Why an answer set was refused. Each names a different way it fails to answer. */\nexport type AnswerRefusal = \"answers-unreadable\" | \"answers-mismatch\" | \"answers-unevidenced\";\n\nexport type AnswerCheck =\n /** The answers, re-ordered to the checklist, one per claim. */\n | { ok: true; answers: Answer[] }\n | { ok: false; code: AnswerRefusal; message: string };\n\n/**\n * Accept an answer set only if it answers THIS checklist: every claim once, no\n * claim twice, nothing extra, and no \"confirmed\" that shows nothing.\n *\n * The refusals are the point of the command. If a stale or partial answer file\n * could be recorded, `verification.yaml` would say a claim was checked when\n * nobody ever asked it — and a record that can lie is worse than no record,\n * because it looks like evidence.\n *\n * `runnerOwned` is the composition rule under `--results` and\n * `--contract-results`: ids a mechanical report answers — scenario claims under\n * the first flag, api.exposes claims under the second — which an answers file\n * must therefore not touch. An entry naming one refuses with its own diagnosis\n * — the id IS on the feature's checklist, so calling it unknown would send the\n * caller hunting a staleness that is not there. `--diff-answers`\n * (commands/verify/cross/diff.ts) calls this with it UNSET on purpose: under a\n * diff both agents answer everything in scope, scenario claims included,\n * because the diff consults no report — so making scenario claims runner-only\n * even without `--results` would break that lens.\n *\n * `context` is what turns the last line from a circle into an instruction. In a\n * fleet the commonest way to \"miss\" a claim is that it is not yours: the\n * checklist spans ten repositories and this one can only answer its own. Told\n * only to \"answer the claims it lists\", an agent in the payment-service repo\n * re-runs verify, sees the same checkout-web claims it cannot speak for, and\n * either loops or invents evidence. So when every unanswered claim belongs to\n * another service, say THAT, and name the command that records them.\n */\nexport function checkAnswers(\n claims: Claim[],\n raw: unknown,\n runnerOwned?: ReadonlySet<string>,\n context?: { feature: string; service?: string },\n): AnswerCheck {\n const refuse = (code: AnswerRefusal, message: string): AnswerCheck => ({ ok: false, code, message });\n\n const list = answerEntries(raw);\n if (list === null) {\n return refuse(\n \"answers-unreadable\",\n \"Expected a JSON array of answers, or an object with an `answers` array: \" +\n `[{ \"id\": \"...\", \"verdict\": \"confirmed\", \"evidence\": [\"file:line\"] }]`,\n );\n }\n\n const answers: Answer[] = [];\n for (const [i, entry] of list.entries()) {\n const where = `answer ${i + 1}`;\n if (!isRecord(entry)) return refuse(\"answers-unreadable\", `${where} is not an object`);\n const id = entry[\"id\"];\n if (typeof id !== \"string\" || id.length === 0) {\n return refuse(\"answers-unreadable\", `${where} has no claim id`);\n }\n const verdict = entry[\"verdict\"];\n if (typeof verdict !== \"string\" || !(VERDICTS as readonly string[]).includes(verdict)) {\n return refuse(\n \"answers-unreadable\",\n `${where} ('${id}') has verdict ${JSON.stringify(verdict)} — expected one of ${VERDICTS.join(\", \")}`,\n );\n }\n const note = typeof entry[\"note\"] === \"string\" ? entry[\"note\"].trim() : \"\";\n answers.push({\n id,\n verdict: verdict as Verdict,\n evidence: stringList(entry[\"evidence\"]),\n ...(note.length > 0 ? { note } : {}),\n answered_by: \"agent\",\n });\n }\n\n const byId = new Map<string, Answer>();\n const twice: string[] = [];\n for (const a of answers) {\n if (byId.has(a.id)) twice.push(a.id);\n else byId.set(a.id, a);\n }\n const known = new Set(claims.map((c) => c.id));\n const strayIds = [...new Set(answers.map((a) => a.id).filter((id) => !known.has(id)))];\n const runnerHit = strayIds.filter((id) => runnerOwned?.has(id) === true);\n const unknown = strayIds.filter((id) => runnerOwned?.has(id) !== true);\n const missingClaims = claims.filter((c) => !byId.has(c.id));\n const missing = missingClaims.map((c) => c.id);\n if (runnerHit.length + unknown.length + missing.length + twice.length > 0) {\n const parts = [\n runnerHit.length > 0\n ? `${runnerHit.length} answer(s) name claim(s) a report owns under --results or --contract-results: ${runnerHit.join(\", \")} — the report answers those; take them out of the answers file`\n : \"\",\n unknown.length > 0 ? `${unknown.length} answer(s) name a claim that is not on the checklist: ${unknown.join(\", \")}` : \"\",\n missing.length > 0 ? `${missing.length} claim(s) have no answer: ${missing.join(\", \")}` : \"\",\n twice.length > 0 ? `${twice.length} claim(s) answered more than once: ${[...new Set(twice)].join(\", \")}` : \"\",\n ].filter((s) => s.length > 0);\n return refuse(\n \"answers-mismatch\",\n `These answers do not match the checklist — ${parts.join(\"; \")}. ` +\n missingAdvice(missingClaims, claims.filter((c) => byId.has(c.id)), context),\n );\n }\n\n const unevidenced = claims\n .map((c) => byId.get(c.id)!)\n .filter((a) => a.verdict === \"confirmed\" && a.evidence.length === 0)\n .map((a) => a.id);\n if (unevidenced.length > 0) {\n return refuse(\n \"answers-unevidenced\",\n `${unevidenced.length} claim(s) are confirmed with no evidence: ${unevidenced.join(\", \")}. A confirmation with nothing behind it is an assertion — give a file:line, or answer 'unconfirmed'.`,\n );\n }\n\n // Checklist order, not the order they were written in: the record reads in the\n // order the questions were asked, whatever the agent did.\n return { ok: true, answers: claims.map((c) => byId.get(c.id)!) };\n}\n\n/**\n * The last sentence of an `answers-mismatch`. \"Re-run and answer the claims it\n * lists\" is sound advice only when the caller CAN answer them. In a fleet the\n * checklist is federated — a feature's claims are filed under the service whose\n * code answers them — so the honest diagnosis for a repo that answered\n * everything it owns is \"the rest is not yours\", together with the form that\n * records the rest. Anything else sends the caller round the same loop.\n */\nfunction missingAdvice(\n missing: Claim[],\n answered: Claim[],\n context?: { feature: string; service?: string },\n): string {\n const generic = \"Re-run `loam verify` and answer the claims it lists.\";\n if (context === undefined || missing.length === 0 || answered.length === 0) return generic;\n // The signature of \"I answered mine and the rest is not mine\": the services\n // that own the unanswered claims and the services that own the answered ones\n // do not overlap. A single-service checklist with one claim forgotten fails\n // this test and keeps the generic advice, which is the right advice there —\n // that claim really is the caller's to answer.\n const owners = [...new Set(missing.map((c) => c.subject))].sort();\n const covered = new Set(answered.map((c) => c.subject));\n if (owners.some((s) => covered.has(s))) return generic;\n const form = `\\`loam verify ${context.feature} --record answers.json --service <svc>\\``;\n const mine = context.service;\n return mine === undefined\n ? `Those claims are owned by ${owners.join(\", \")} — each service attests its own from its own repository (${form}); ` +\n \"this all-at-once form has to answer the whole checklist in one file.\"\n : `Every unanswered claim belongs to a different service (${owners.join(\", \")}) and this repository can only attest '${mine}' — ` +\n `run ${form} in each of those repositories instead.`;\n}\n\nfunction stringList(v: unknown): string[] {\n if (typeof v === \"string\") return v.trim().length > 0 ? [v.trim()] : [];\n if (!Array.isArray(v)) return [];\n return v.map((x) => String(x).trim()).filter((s) => s.length > 0);\n}\n\n"]}
@@ -6,7 +6,10 @@
6
6
  * because the two disagree about what an unanswered claim means: answering
7
7
  * everything at once, a missing answer is a gap in this run, while a federated
8
8
  * record is assembled from attestations that each covered one service and were
9
- * never expected to cover the rest.
9
+ * never expected to cover the rest. They also land in different places: the
10
+ * all-at-once record is the one file `verification.yaml`, while each service's
11
+ * attestation is a file of its own under `verification/` (`./location.ts` says
12
+ * why, on `verificationDir`).
10
13
  */
11
14
  import {} from "./checklist.js";
12
15
  import {} from "./answers.js";
@@ -47,104 +50,121 @@ export function buildVerification(checklist, answers, today, reports = {}) {
47
50
  ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),
48
51
  };
49
52
  }
50
- export function buildFederatedVerification(checklist, attestation, answers, previous) {
53
+ /**
54
+ * One service's attestation, as the file only that service ever writes.
55
+ *
56
+ * This used to be a merge: the new attestation spliced into the one shared
57
+ * record, every other service's answers retained, pruned to the current
58
+ * checklist and re-sorted — and that rewrite of everybody's bytes is exactly
59
+ * what made two teams' docs PRs conflict. Now nothing else is touched. The
60
+ * other services' files stand as they are, the union is added up at read time
61
+ * (`read/union.ts`), and an answer of theirs the feature no longer asks is
62
+ * simply not counted there rather than pruned here.
63
+ *
64
+ * The file is a whole schema-2 record with one attestation, so it is graded
65
+ * with the same shape check as every record and reads without loam: its
66
+ * `summary` counts the whole checklist, with the claims other services answer
67
+ * honestly `unanswered` in it.
68
+ */
69
+ export function buildServiceRecord(checklist, attestation, answers, previous) {
51
70
  const { service, recorded, commit, reports, docsCommit } = attestation;
52
- const currentById = new Map(checklist.claims.map((claim) => [claim.id, claim]));
53
- const localIds = new Set(checklist.claims.filter((claim) => claim.subject === service).map((claim) => claim.id));
54
- // An old all-at-once record remains readable, but its answers have no commit
55
- // attestation and therefore cannot silently become somebody else's
56
- // federated contribution. The first schema-2 write starts a clean federation.
57
- const previouslyAttested = new Set((previous?.attestations ?? []).flatMap((a) => a.claims));
58
- const retained = (previous?.schema === 2 ? previous.claims : []).filter((claim) => {
59
- const current = currentById.get(claim.id);
60
- return current !== undefined && current.subject !== service && previouslyAttested.has(claim.id);
61
- });
62
- const local = answers.map((answer) => {
71
+ const byId = new Map(answers.map((answer) => [answer.id, answer]));
72
+ const currentIds = new Set(checklist.claims.map((claim) => claim.id));
73
+ for (const answer of answers) {
63
74
  // Same contract as `buildVerification`: an answer is built from the live
64
75
  // checklist, so it always names a current claim. Off-checklist answers do
65
76
  // NOT belong here — `discarded` means "an answer the PREVIOUS record held",
66
77
  // and routing an incoming stray into it would change what the printed
67
78
  // notice tells an operator. A caller that reaches this gets told which
68
- // answer it was rather than a `TypeError` on `claim.kind`.
69
- const claim = currentById.get(answer.id);
70
- if (claim === undefined) {
71
- throw new Error(`buildFederatedVerification: answer ${answer.id} names no current claim`);
79
+ // answer it was rather than an answer silently missing from the file.
80
+ if (!currentIds.has(answer.id)) {
81
+ throw new Error(`buildServiceRecord: answer ${answer.id} names no current claim`);
72
82
  }
73
- return {
74
- id: claim.id,
75
- kind: claim.kind,
76
- subject: claim.subject,
77
- claim: claim.claim,
78
- verdict: answer.verdict,
79
- answered_by: answer.answered_by,
80
- evidence: answer.evidence,
81
- ...(answer.note === undefined ? {} : { note: answer.note }),
82
- // The pins the evidence validation stamped onto this repository's
83
- // answers ride into the record here; retained other-service claims are
84
- // spread whole below, so THEIR pins survive a re-record untouched.
85
- ...(answer.evidence_pins === undefined ? {} : { evidence_pins: answer.evidence_pins }),
86
- };
87
- });
88
- const byId = new Map([...retained, ...local].map((claim) => [claim.id, claim]));
83
+ }
89
84
  const claims = checklist.claims.flatMap((claim) => {
90
85
  const answer = byId.get(claim.id);
91
86
  if (answer === undefined)
92
87
  return [];
93
- // Normalize retained legacy entries to the current question text and add
94
- // the subject schema 2 needs for future independent replacement.
95
- return [{ ...answer, kind: claim.kind, subject: claim.subject, claim: claim.claim }];
96
- });
97
- const retainedAttestations = (previous?.attestations ?? []).flatMap((attestation) => {
98
- if (attestation.service === service)
99
- return [];
100
- const ids = attestation.claims.filter((id) => currentById.get(id)?.subject === attestation.service && byId.has(id));
101
- return ids.length === 0 ? [] : [{ ...attestation, claims: ids }];
88
+ return [
89
+ {
90
+ id: claim.id,
91
+ kind: claim.kind,
92
+ subject: claim.subject,
93
+ claim: claim.claim,
94
+ verdict: answer.verdict,
95
+ answered_by: answer.answered_by,
96
+ evidence: answer.evidence,
97
+ ...(answer.note === undefined ? {} : { note: answer.note }),
98
+ // The pins the evidence validation stamped onto this repository's
99
+ // answers ride into the file here; no other service's file is
100
+ // rewritten, so THEIR pins are never touched at all.
101
+ ...(answer.evidence_pins === undefined ? {} : { evidence_pins: answer.evidence_pins }),
102
+ },
103
+ ];
102
104
  });
103
- const attestations = [
104
- ...retainedAttestations,
105
- {
106
- service,
107
- commit,
108
- recorded,
109
- claims: checklist.claims.filter((c) => localIds.has(c.id)).map((c) => c.id),
110
- // What THIS repository answered, and what it answered it against. Both
111
- // ride with the attestation rather than the record for the reason the
112
- // reports do: they are one repository's facts, pruned and retained with
113
- // that repository's answers.
114
- checklist: checklist.digest,
115
- ...(docsCommit === undefined ? {} : { docsCommit }),
116
- // Filed with the attestation, not the record: each report is one
117
- // repository's run, and it is pruned or retained with that repository's
118
- // answers rather than outliving them — the contract report under the
119
- // same rule as the cucumber one.
120
- ...(reports.results === undefined ? {} : { report: reports.results }),
121
- ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),
122
- },
123
- ].sort((a, b) => a.service.localeCompare(b.service));
124
105
  const confirmed = claims.filter((claim) => claim.verdict === "confirmed").length;
125
- const unconfirmed = claims.length - confirmed;
126
- const unanswered = checklist.claims.length - claims.length;
127
- // Everything the old record answered that the new one does not. The caller
128
- // prints it; nothing here decides it is acceptable.
129
- const discarded = (previous?.claims ?? [])
130
- .filter((claim) => !byId.has(claim.id))
106
+ const record = {
107
+ schema: 2,
108
+ feature: checklist.feature,
109
+ recorded,
110
+ checklist: checklist.digest,
111
+ summary: {
112
+ claims: checklist.claims.length,
113
+ confirmed,
114
+ unconfirmed: claims.length - confirmed,
115
+ unanswered: checklist.claims.length - claims.length,
116
+ },
117
+ claims,
118
+ attestations: [
119
+ {
120
+ service,
121
+ commit,
122
+ recorded,
123
+ claims: checklist.claims.filter((c) => c.subject === service).map((c) => c.id),
124
+ // What THIS repository answered, and what it answered it against.
125
+ // `checklist` is what `verify.checklist-forked` and the union's
126
+ // one-version rule read; `docsCommit` pins the side the question came
127
+ // from.
128
+ checklist: checklist.digest,
129
+ ...(docsCommit === undefined ? {} : { docsCommit }),
130
+ // Each report is one repository's run, filed with that repository's
131
+ // attestation — the contract report under the same rule as the
132
+ // cucumber one.
133
+ ...(reports.results === undefined ? {} : { report: reports.results }),
134
+ ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),
135
+ },
136
+ ],
137
+ };
138
+ const { legacy, own, filed } = previous;
139
+ const federatedLegacy = legacy?.schema === 2;
140
+ const dropLegacy = legacy !== null &&
141
+ (!federatedLegacy || (legacy.attestations ?? []).every((a) => a.service === service || filed.has(a.service)));
142
+ // Everything this write takes off the disk that the new file does not carry:
143
+ // this service's own previous answers (from its file, or from its share of a
144
+ // one-file record no file of its own had superseded yet) and, when the
145
+ // one-file record is dropped, an all-at-once record's answers. Another
146
+ // service's answers are never in this list — their files are not touched.
147
+ const replaced = [
148
+ ...(own?.claims ?? []),
149
+ ...(legacy === null
150
+ ? []
151
+ : !federatedLegacy
152
+ ? legacy.claims
153
+ : own === null
154
+ ? legacy.claims.filter((claim) => claim.subject === service)
155
+ : []),
156
+ ];
157
+ const carried = new Set(claims.map((claim) => claim.id));
158
+ // Keyed by id: a service's own file and its share of the one-file record
159
+ // can both hold the same stale answer, and it is one answer.
160
+ const dropped = new Map(replaced.filter((claim) => !carried.has(claim.id)).map((claim) => [claim.id, claim]));
161
+ const discarded = [...dropped.values()]
131
162
  .map((claim) => ({
132
163
  id: claim.id,
133
164
  claim: claim.claim,
134
165
  ...(claim.subject === undefined ? {} : { subject: claim.subject }),
135
- reason: currentById.has(claim.id) ? "unattested" : "off-checklist",
166
+ reason: currentIds.has(claim.id) ? "unattested" : "off-checklist",
136
167
  }));
137
- return {
138
- verification: {
139
- schema: 2,
140
- feature: checklist.feature,
141
- recorded,
142
- checklist: checklist.digest,
143
- summary: { claims: checklist.claims.length, confirmed, unconfirmed, unanswered },
144
- claims,
145
- attestations,
146
- },
147
- discarded,
148
- };
168
+ return { record, dropLegacy, discarded };
149
169
  }
150
170
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/core/verify/build.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Checklist plus answers becomes a record — once for a feature answered all at\n * once, once for a fleet answering service by service.\n *\n * The federated form is separate from `buildVerification` and not a flag on it\n * because the two disagree about what an unanswered claim means: answering\n * everything at once, a missing answer is a gap in this run, while a federated\n * record is assembled from attestations that each covered one service and were\n * never expected to cover the rest.\n */\nimport { type Checklist } from \"./checklist.js\";\nimport { type Answer } from \"./answers.js\";\nimport {\n type ConsumedReports,\n type RecordedClaim,\n type ServiceAttestation,\n type Verification,\n} from \"./record.js\";\n\nexport function buildVerification(\n checklist: Checklist,\n answers: Answer[],\n today: string,\n reports: ConsumedReports = {},\n): Verification {\n const byId = new Map(answers.map((a) => [a.id, a]));\n const claims: RecordedClaim[] = checklist.claims.map((c) => {\n // Callers hand in an answer per claim — `runnerAnswers` is total over the\n // checklist, and both other callers map the checklist to build theirs. The\n // branch is here because the alternative to a diagnosis is a `TypeError` on\n // `a.verdict`, which cli.ts reports as `internal` with a stack about a\n // property of `undefined` and no word about which claim went missing.\n const a = byId.get(c.id);\n if (a === undefined) throw new Error(`buildVerification: no answer for claim ${c.id}`);\n // Deliberately no `evidence_pins` here: the legacy all-at-once form binds\n // to no commit, so there is no attested blob to pin — and none is ever\n // handed in, because pins are computed inside the federated evidence\n // validation this form never runs.\n return {\n id: c.id,\n kind: c.kind,\n claim: c.claim,\n verdict: a.verdict,\n answered_by: a.answered_by,\n evidence: a.evidence,\n ...(a.note === undefined ? {} : { note: a.note }),\n };\n });\n const confirmed = claims.filter((c) => c.verdict === \"confirmed\").length;\n return {\n feature: checklist.feature,\n recorded: today,\n checklist: checklist.digest,\n summary: { claims: claims.length, confirmed, unconfirmed: claims.length - confirmed },\n claims,\n ...(reports.results === undefined ? {} : { report: reports.results }),\n ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),\n };\n}\n\n/**\n * An answer the previous record held that the new one does not carry.\n *\n * Dropping these is correct — see `buildFederatedVerification` — but doing it\n * silently is not: a schema-1 record answers the WHOLE checklist, so the first\n * federated write turns a green record into a partial one, and an operator who\n * is not told which answers went missing reads the drop as loam losing data.\n */\nexport interface DiscardedAnswer {\n id: string;\n claim: string;\n /** The service the record filed it under, when it said. Absent in schema-1 records. */\n subject?: string;\n /**\n * `off-checklist` — the feature changed and nobody asks this question any\n * more. `unattested` — the question still stands, but no service attestation\n * binds the answer to a commit, so it cannot be carried into a federated\n * record; the owning repository has to record it again.\n */\n reason: \"off-checklist\" | \"unattested\";\n}\n\nexport interface FederatedBuild {\n verification: Verification;\n /** In checklist-independent record order — see {@link DiscardedAnswer}. */\n discarded: DiscardedAnswer[];\n}\n\n/**\n * Replace one service's contribution while retaining current contributions\n * from other repositories. Claim ids removed from the live checklist are\n * pruned from both the answers and their attestations, so a changed feature can\n * never keep a green answer to a question it no longer asks.\n */\n/** One service's attestation: whose word it is, when, and against which commit. */\nexport interface Attestation {\n service: string;\n recorded: string;\n commit: string;\n reports: ConsumedReports;\n /** The docs-repo HEAD the checklist was derived from, when git could answer. */\n docsCommit?: string;\n}\n\nexport function buildFederatedVerification(\n checklist: Checklist,\n attestation: Attestation,\n answers: Answer[],\n previous: Verification | null,\n): FederatedBuild {\n const { service, recorded, commit, reports, docsCommit } = attestation;\n const currentById = new Map(checklist.claims.map((claim) => [claim.id, claim]));\n const localIds = new Set(checklist.claims.filter((claim) => claim.subject === service).map((claim) => claim.id));\n\n // An old all-at-once record remains readable, but its answers have no commit\n // attestation and therefore cannot silently become somebody else's\n // federated contribution. The first schema-2 write starts a clean federation.\n const previouslyAttested = new Set((previous?.attestations ?? []).flatMap((a) => a.claims));\n const retained = (previous?.schema === 2 ? previous.claims : []).filter((claim) => {\n const current = currentById.get(claim.id);\n return current !== undefined && current.subject !== service && previouslyAttested.has(claim.id);\n });\n const local = answers.map((answer): RecordedClaim => {\n // Same contract as `buildVerification`: an answer is built from the live\n // checklist, so it always names a current claim. Off-checklist answers do\n // NOT belong here — `discarded` means \"an answer the PREVIOUS record held\",\n // and routing an incoming stray into it would change what the printed\n // notice tells an operator. A caller that reaches this gets told which\n // answer it was rather than a `TypeError` on `claim.kind`.\n const claim = currentById.get(answer.id);\n if (claim === undefined) {\n throw new Error(`buildFederatedVerification: answer ${answer.id} names no current claim`);\n }\n return {\n id: claim.id,\n kind: claim.kind,\n subject: claim.subject,\n claim: claim.claim,\n verdict: answer.verdict,\n answered_by: answer.answered_by,\n evidence: answer.evidence,\n ...(answer.note === undefined ? {} : { note: answer.note }),\n // The pins the evidence validation stamped onto this repository's\n // answers ride into the record here; retained other-service claims are\n // spread whole below, so THEIR pins survive a re-record untouched.\n ...(answer.evidence_pins === undefined ? {} : { evidence_pins: answer.evidence_pins }),\n };\n });\n const byId = new Map([...retained, ...local].map((claim) => [claim.id, claim]));\n const claims = checklist.claims.flatMap((claim) => {\n const answer = byId.get(claim.id);\n if (answer === undefined) return [];\n // Normalize retained legacy entries to the current question text and add\n // the subject schema 2 needs for future independent replacement.\n return [{ ...answer, kind: claim.kind, subject: claim.subject, claim: claim.claim }];\n });\n\n const retainedAttestations = (previous?.attestations ?? []).flatMap((attestation) => {\n if (attestation.service === service) return [];\n const ids = attestation.claims.filter(\n (id) => currentById.get(id)?.subject === attestation.service && byId.has(id),\n );\n return ids.length === 0 ? [] : [{ ...attestation, claims: ids }];\n });\n const attestations: ServiceAttestation[] = [\n ...retainedAttestations,\n {\n service,\n commit,\n recorded,\n claims: checklist.claims.filter((c) => localIds.has(c.id)).map((c) => c.id),\n // What THIS repository answered, and what it answered it against. Both\n // ride with the attestation rather than the record for the reason the\n // reports do: they are one repository's facts, pruned and retained with\n // that repository's answers.\n checklist: checklist.digest,\n ...(docsCommit === undefined ? {} : { docsCommit }),\n // Filed with the attestation, not the record: each report is one\n // repository's run, and it is pruned or retained with that repository's\n // answers rather than outliving them — the contract report under the\n // same rule as the cucumber one.\n ...(reports.results === undefined ? {} : { report: reports.results }),\n ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),\n },\n ].sort((a, b) => a.service.localeCompare(b.service));\n\n const confirmed = claims.filter((claim) => claim.verdict === \"confirmed\").length;\n const unconfirmed = claims.length - confirmed;\n const unanswered = checklist.claims.length - claims.length;\n\n // Everything the old record answered that the new one does not. The caller\n // prints it; nothing here decides it is acceptable.\n const discarded = (previous?.claims ?? [])\n .filter((claim) => !byId.has(claim.id))\n .map((claim): DiscardedAnswer => ({\n id: claim.id,\n claim: claim.claim,\n ...(claim.subject === undefined ? {} : { subject: claim.subject }),\n reason: currentById.has(claim.id) ? \"unattested\" : \"off-checklist\",\n }));\n\n return {\n verification: {\n schema: 2,\n feature: checklist.feature,\n recorded,\n checklist: checklist.digest,\n summary: { claims: checklist.claims.length, confirmed, unconfirmed, unanswered },\n claims,\n attestations,\n },\n discarded,\n };\n}\n"]}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/core/verify/build.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Checklist plus answers becomes a record — once for a feature answered all at\n * once, once for a fleet answering service by service.\n *\n * The federated form is separate from `buildVerification` and not a flag on it\n * because the two disagree about what an unanswered claim means: answering\n * everything at once, a missing answer is a gap in this run, while a federated\n * record is assembled from attestations that each covered one service and were\n * never expected to cover the rest. They also land in different places: the\n * all-at-once record is the one file `verification.yaml`, while each service's\n * attestation is a file of its own under `verification/` (`./location.ts` says\n * why, on `verificationDir`).\n */\nimport { type Checklist } from \"./checklist.js\";\nimport { type Answer } from \"./answers.js\";\nimport { type ConsumedReports, type RecordedClaim, type Verification } from \"./record.js\";\n\nexport function buildVerification(\n checklist: Checklist,\n answers: Answer[],\n today: string,\n reports: ConsumedReports = {},\n): Verification {\n const byId = new Map(answers.map((a) => [a.id, a]));\n const claims: RecordedClaim[] = checklist.claims.map((c) => {\n // Callers hand in an answer per claim — `runnerAnswers` is total over the\n // checklist, and both other callers map the checklist to build theirs. The\n // branch is here because the alternative to a diagnosis is a `TypeError` on\n // `a.verdict`, which cli.ts reports as `internal` with a stack about a\n // property of `undefined` and no word about which claim went missing.\n const a = byId.get(c.id);\n if (a === undefined) throw new Error(`buildVerification: no answer for claim ${c.id}`);\n // Deliberately no `evidence_pins` here: the legacy all-at-once form binds\n // to no commit, so there is no attested blob to pin — and none is ever\n // handed in, because pins are computed inside the federated evidence\n // validation this form never runs.\n return {\n id: c.id,\n kind: c.kind,\n claim: c.claim,\n verdict: a.verdict,\n answered_by: a.answered_by,\n evidence: a.evidence,\n ...(a.note === undefined ? {} : { note: a.note }),\n };\n });\n const confirmed = claims.filter((c) => c.verdict === \"confirmed\").length;\n return {\n feature: checklist.feature,\n recorded: today,\n checklist: checklist.digest,\n summary: { claims: claims.length, confirmed, unconfirmed: claims.length - confirmed },\n claims,\n ...(reports.results === undefined ? {} : { report: reports.results }),\n ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),\n };\n}\n\n/**\n * An answer the previous record held that the new one does not carry.\n *\n * Dropping these is correct — see `buildServiceRecord` — but doing it silently\n * is not: a schema-1 record answers the WHOLE checklist, so the first\n * federated write turns a green record into a partial one, and an operator who\n * is not told which answers went missing reads the drop as loam losing data.\n */\nexport interface DiscardedAnswer {\n id: string;\n claim: string;\n /** The service the record filed it under, when it said. Absent in schema-1 records. */\n subject?: string;\n /**\n * `off-checklist` — the feature changed and nobody asks this question any\n * more. `unattested` — the question still stands, but no service attestation\n * binds the answer to a commit, so it cannot be carried into a federated\n * record; the owning repository has to record it again.\n */\n reason: \"off-checklist\" | \"unattested\";\n}\n\n/** One service's attestation: whose word it is, when, and against which commit. */\nexport interface Attestation {\n service: string;\n recorded: string;\n commit: string;\n reports: ConsumedReports;\n /** The docs-repo HEAD the checklist was derived from, when git could answer. */\n docsCommit?: string;\n}\n\n/**\n * The record files a service's write replaces, as the authoritative read found\n * them: the one-file record, and this service's own `verification/<svc>.yaml`.\n * `filed` names every service that already has a file of its own — what\n * decides whether anything in the one-file record still counts.\n */\nexport interface PreviousFiles {\n legacy: Verification | null;\n own: Verification | null;\n filed: ReadonlySet<string>;\n}\n\nexport interface ServiceBuild {\n /** `verification/<svc>.yaml` as it will be written: this service's attestation and answers, nobody else's. */\n record: Verification;\n /**\n * Whether the one-file record goes in the same commit, because nothing in it\n * counts any more: an all-at-once record never counts once a service has a\n * file (its answers carry no attestation — today's write used to overwrite it\n * for the same reason), and a federated one stops counting once every\n * service it names has a file of its own.\n */\n dropLegacy: boolean;\n /** In record order — see {@link DiscardedAnswer}. */\n discarded: DiscardedAnswer[];\n}\n\n/**\n * One service's attestation, as the file only that service ever writes.\n *\n * This used to be a merge: the new attestation spliced into the one shared\n * record, every other service's answers retained, pruned to the current\n * checklist and re-sorted — and that rewrite of everybody's bytes is exactly\n * what made two teams' docs PRs conflict. Now nothing else is touched. The\n * other services' files stand as they are, the union is added up at read time\n * (`read/union.ts`), and an answer of theirs the feature no longer asks is\n * simply not counted there rather than pruned here.\n *\n * The file is a whole schema-2 record with one attestation, so it is graded\n * with the same shape check as every record and reads without loam: its\n * `summary` counts the whole checklist, with the claims other services answer\n * honestly `unanswered` in it.\n */\nexport function buildServiceRecord(\n checklist: Checklist,\n attestation: Attestation,\n answers: Answer[],\n previous: PreviousFiles,\n): ServiceBuild {\n const { service, recorded, commit, reports, docsCommit } = attestation;\n const byId = new Map(answers.map((answer) => [answer.id, answer]));\n const currentIds = new Set(checklist.claims.map((claim) => claim.id));\n for (const answer of answers) {\n // Same contract as `buildVerification`: an answer is built from the live\n // checklist, so it always names a current claim. Off-checklist answers do\n // NOT belong here — `discarded` means \"an answer the PREVIOUS record held\",\n // and routing an incoming stray into it would change what the printed\n // notice tells an operator. A caller that reaches this gets told which\n // answer it was rather than an answer silently missing from the file.\n if (!currentIds.has(answer.id)) {\n throw new Error(`buildServiceRecord: answer ${answer.id} names no current claim`);\n }\n }\n const claims = checklist.claims.flatMap((claim): RecordedClaim[] => {\n const answer = byId.get(claim.id);\n if (answer === undefined) return [];\n return [\n {\n id: claim.id,\n kind: claim.kind,\n subject: claim.subject,\n claim: claim.claim,\n verdict: answer.verdict,\n answered_by: answer.answered_by,\n evidence: answer.evidence,\n ...(answer.note === undefined ? {} : { note: answer.note }),\n // The pins the evidence validation stamped onto this repository's\n // answers ride into the file here; no other service's file is\n // rewritten, so THEIR pins are never touched at all.\n ...(answer.evidence_pins === undefined ? {} : { evidence_pins: answer.evidence_pins }),\n },\n ];\n });\n const confirmed = claims.filter((claim) => claim.verdict === \"confirmed\").length;\n const record: Verification = {\n schema: 2,\n feature: checklist.feature,\n recorded,\n checklist: checklist.digest,\n summary: {\n claims: checklist.claims.length,\n confirmed,\n unconfirmed: claims.length - confirmed,\n unanswered: checklist.claims.length - claims.length,\n },\n claims,\n attestations: [\n {\n service,\n commit,\n recorded,\n claims: checklist.claims.filter((c) => c.subject === service).map((c) => c.id),\n // What THIS repository answered, and what it answered it against.\n // `checklist` is what `verify.checklist-forked` and the union's\n // one-version rule read; `docsCommit` pins the side the question came\n // from.\n checklist: checklist.digest,\n ...(docsCommit === undefined ? {} : { docsCommit }),\n // Each report is one repository's run, filed with that repository's\n // attestation — the contract report under the same rule as the\n // cucumber one.\n ...(reports.results === undefined ? {} : { report: reports.results }),\n ...(reports.contract === undefined ? {} : { contractReport: reports.contract }),\n },\n ],\n };\n\n const { legacy, own, filed } = previous;\n const federatedLegacy = legacy?.schema === 2;\n const dropLegacy =\n legacy !== null &&\n (!federatedLegacy || (legacy.attestations ?? []).every((a) => a.service === service || filed.has(a.service)));\n // Everything this write takes off the disk that the new file does not carry:\n // this service's own previous answers (from its file, or from its share of a\n // one-file record no file of its own had superseded yet) and, when the\n // one-file record is dropped, an all-at-once record's answers. Another\n // service's answers are never in this list — their files are not touched.\n const replaced = [\n ...(own?.claims ?? []),\n ...(legacy === null\n ? []\n : !federatedLegacy\n ? legacy.claims\n : own === null\n ? legacy.claims.filter((claim) => claim.subject === service)\n : []),\n ];\n const carried = new Set(claims.map((claim) => claim.id));\n // Keyed by id: a service's own file and its share of the one-file record\n // can both hold the same stale answer, and it is one answer.\n const dropped = new Map(replaced.filter((claim) => !carried.has(claim.id)).map((claim) => [claim.id, claim]));\n const discarded = [...dropped.values()]\n .map((claim): DiscardedAnswer => ({\n id: claim.id,\n claim: claim.claim,\n ...(claim.subject === undefined ? {} : { subject: claim.subject }),\n reason: currentIds.has(claim.id) ? \"unattested\" : \"off-checklist\",\n }));\n return { record, dropLegacy, discarded };\n}\n"]}
@@ -28,9 +28,10 @@ import { readFile } from "node:fs/promises";
28
28
  import { readAsyncapi } from "../asyncapi/read.js";
29
29
  import {} from "../c4/likec4.js";
30
30
  import { elementService, serviceResolver } from "../c4/resolve/service.js";
31
- import { ACTOR_KINDS } from "../vocabulary/maturity.js";
31
+ import { livingMapOf, readDeltaSystem } from "../c4/resolve/systems.js";
32
+ import { scenarioGherkin } from "../vocabulary/steps.js";
32
33
  import { operationIds, operations } from "../openapi/doc.js";
33
- import { featurePaths, featureSpecPaths } from "../repo/paths.js";
34
+ import { featurePaths, featureSpecPaths, landscapePath } from "../repo/paths.js";
34
35
  import { locateServicePaths } from "../repo/service-target.js";
35
36
  import { featureSpecServices } from "../repo/repo.js";
36
37
  import { enumeratedServiceIds } from "../repo/service-target.js";
@@ -44,7 +45,13 @@ import { checklistDigest, claimId, DIGEST_LENGTH, ID_LENGTH, } from "./claims/id
44
45
  /**
45
46
  * Derive the checklist from a feature's own artifacts. No code is read here and
46
47
  * none should be: the whole design rests on the questions being computed from
47
- * documents that do not change between runs.
48
+ * documents that do not change between runs. Those are the feature's own
49
+ * files, read against the fleet they merge into — the `services/`
50
+ * enumeration, which says which elements stand for a service, and, for a
51
+ * tagged element no directory answers for, the living map, whose tags the
52
+ * merge keeps (`../c4/resolve/systems.ts`). A map that does not parse
53
+ * leaves the delta to answer alone, which is the answer given before the map
54
+ * was asked.
48
55
  */
49
56
  export async function featureChecklist(docsDir, featureDir, featureId, fleet) {
50
57
  // The per-service loop below resolves living contracts through the
@@ -78,25 +85,74 @@ export async function featureChecklist(docsDir, featureDir, featureId, fleet) {
78
85
  // reports it; inventing claims out of a broken document would only add noise.
79
86
  if (res.errors.length === 0) {
80
87
  const elements = res.elements;
88
+ // The enumerated fleet — `services/` plus this feature's own `specs/`
89
+ // directories, the only place a service the feature INTRODUCES has a
90
+ // directory at all. Built once, above both readers below, because they
91
+ // must not disagree: the skip loop asks whether an element STANDS FOR a
92
+ // service, and the resolver asks WHICH service an endpoint belongs to,
93
+ // and a set that answered the second question only left the first one
94
+ // reading a title as unknown that the very next line resolved.
95
+ const living = new Set(await enumeratedServiceIds(docsDir, ctx));
96
+ const known = new Set([...living, ...specServices]);
97
+ // The living map, parsed only for a tagged element no directory answers for.
98
+ const systems = { elements, known, living, livingMap: livingMapOf(landscapePath(docsDir), (path) => ctx.loadLikeC4(path)) };
81
99
  for (const e of elements) {
82
100
  if (!e.tags.includes(featureId))
83
101
  continue;
84
- // A dotted id is a container INSIDE a service, and an actor is nobody's
85
- // repository. Neither is a service that has to exist.
86
- if (e.id.includes(".") || ACTOR_KINDS.has(e.kind.toLowerCase()))
102
+ // Only a SYSTEM of ours is a service that has to exist — the fleet
103
+ // census's reading, applied to the map this merge leaves
104
+ // (`../c4/resolve/systems.ts` holds it and says what it skips). An
105
+ // element inside a service is its container, an actor is nobody's
106
+ // repository, an `#external` system is somebody else's, a grouping is
107
+ // scenery for the services under it, and the fleet's own
108
+ // infrastructure — a `database`, `queue`, `topic`, `cache` or
109
+ // `bucket` — has no repository either. None of them is a service that
110
+ // has to exist.
111
+ //
112
+ // The infra skip closes a claim no repository could ever answer: a
113
+ // delta that draws a fleet-level `database` minted `service.exists` for
114
+ // it, and `loam verify` then asked somebody to point at the code that
115
+ // proves a Postgres has a services/ directory. The `#external` and
116
+ // grouping skips close the same kind of claim: a tagged producer
117
+ // outside the fleet, or the domain a new service is drawn inside, each
118
+ // became a `loam tasks` unit asking for a service to be built.
119
+ //
120
+ // STANDING FOR A SERVICE still wins over the infra kind, because that is
121
+ // a claim about this repo rather than a guess at one. Both of its rungs
122
+ // matter: a `database` carrying `metadata { service '<id>' }` names a
123
+ // directory the author says exists, and one whose TITLE names a real
124
+ // `services/<id>/` names a directory the enumeration just read. Testing
125
+ // the binding alone made an adopted store a service on every other
126
+ // surface and infrastructure here, so its `service.exists` claim
127
+ // silently went missing from the checklist that is supposed to hold
128
+ // every promise the feature made.
129
+ //
130
+ // DEPTH IS NOT A FACT ABOUT A SERVICE. This used to skip every dotted
131
+ // id as a container, so the new service a delta draws inside the map's
132
+ // own group — FEAT-101 in the shipped example — was never asked about
133
+ // at all. A grouping the delta declares is looked through; any other
134
+ // level is what its children belong to.
135
+ //
136
+ // The stub of a service that already has a directory keeps exactly the
137
+ // answer it always had — asked about when tagged at top level, never
138
+ // when nested — because the question is not this module's to reopen
139
+ // here: nesting it inside the map's group must not turn a service
140
+ // somebody adopted into "create service" at the head of `loam tasks`,
141
+ // and the top-level claim is pinned on purpose (an adopted store named
142
+ // by its title is a promise the feature makes about it).
143
+ const reading = await readDeltaSystem(e, systems);
144
+ if (reading === "none" || (reading === "living" && e.id.includes(".")))
87
145
  continue;
88
146
  const svc = elementService(e);
89
147
  exists.push(claim("service.exists", svc, [svc], `service '${svc}' exists`));
90
148
  }
91
- // The enumerated fleet — `services/` plus this feature's own `specs/`
92
- // directories, the only place a service the feature INTRODUCES has a
93
- // directory at all — rides into the resolver so a claim about an edge
94
- // drawn into a modelled container names the service that owns it. A
95
- // claim against a service called "api" that has never existed is one no
149
+ // That same set rides into the resolver so a claim about an edge drawn
150
+ // into a modelled container names the service that owns it. A claim
151
+ // against a service called "api" that has never existed is one no
96
152
  // evidence in any repository could ever answer, and the fleet half alone
97
153
  // still derived exactly that claim for an introduced service's own
98
154
  // container. Unenumerable services/ degrades to the feature's names.
99
- const svcOf = serviceResolver(elements, new Set([...(await enumeratedServiceIds(docsDir, ctx)), ...specServices]));
155
+ const svcOf = serviceResolver(elements, known);
100
156
  for (const r of res.relationships) {
101
157
  // Untagged edges are context for the diagram; an edge with no operation
102
158
  // names nothing specific enough to look for in the code.
@@ -169,6 +225,7 @@ export async function featureChecklist(docsDir, featureDir, featureId, fleet) {
169
225
  scenarios.push({
170
226
  ...claim("scenario.tested", svc, [svc, r.id ?? r.name, s.name, body.slice(0, ID_LENGTH)], `scenario '${s.name}' of requirement '${r.name}' (${svc}) is covered by a test`),
171
227
  digest: body.slice(0, DIGEST_LENGTH),
228
+ steps: scenarioGherkin(s.lines).steps.length,
172
229
  });
173
230
  }
174
231
  }
@@ -190,6 +247,7 @@ export async function featureChecklist(docsDir, featureDir, featureId, fleet) {
190
247
  scenarios.push({
191
248
  ...claim("scenario.tested", svc, [svc, "arch.spec.md", r.id ?? r.name, s.name, body.slice(0, ID_LENGTH)], `scenario '${s.name}' of arch requirement '${r.name}' (${svc}, arch.spec.md) is covered by a test`),
192
249
  digest: body.slice(0, DIGEST_LENGTH),
250
+ steps: scenarioGherkin(s.lines).steps.length,
193
251
  });
194
252
  }
195
253
  }