@ybotok/loam 0.2.0-alpha.7 → 0.2.0-alpha.9

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 (648) hide show
  1. package/CHANGELOG.md +1315 -1
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +40 -22
  5. package/QUICKSTART.md +181 -9
  6. package/README.md +256 -57
  7. package/ROADMAP.md +83 -7
  8. package/SCHEMA.md +569 -98
  9. package/WORKFLOW.md +272 -62
  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/archive/archive.js +15 -18
  15. package/dist/commands/archive/archive.js.map +1 -1
  16. package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
  17. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  18. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  19. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/openapi.js +25 -42
  21. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
  23. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  24. package/dist/commands/archive/plan/gate.js +3 -7
  25. package/dist/commands/archive/plan/gate.js.map +1 -1
  26. package/dist/commands/archive/plan/landscape.js +4 -4
  27. package/dist/commands/archive/plan/landscape.js.map +1 -1
  28. package/dist/commands/archive/plan/refusal.js +8 -18
  29. package/dist/commands/archive/plan/refusal.js.map +1 -1
  30. package/dist/commands/archive/plan/specs.js +134 -25
  31. package/dist/commands/archive/plan/specs.js.map +1 -1
  32. package/dist/commands/archive/plan/state.js +1 -0
  33. package/dist/commands/archive/plan/state.js.map +1 -1
  34. package/dist/commands/archive/run.js +33 -11
  35. package/dist/commands/archive/run.js.map +1 -1
  36. package/dist/commands/archive/shipped.js +49 -0
  37. package/dist/commands/archive/shipped.js.map +1 -0
  38. package/dist/commands/context/context.js +4 -7
  39. package/dist/commands/context/context.js.map +1 -1
  40. package/dist/commands/context/print.js +2 -2
  41. package/dist/commands/context/print.js.map +1 -1
  42. package/dist/commands/delta/delta.js +44 -12
  43. package/dist/commands/delta/delta.js.map +1 -1
  44. package/dist/commands/delta/print.js +1 -1
  45. package/dist/commands/delta/print.js.map +1 -1
  46. package/dist/commands/dependencies.js +3 -7
  47. package/dist/commands/dependencies.js.map +1 -1
  48. package/dist/commands/diff/diff.js +20 -27
  49. package/dist/commands/diff/diff.js.map +1 -1
  50. package/dist/commands/explore.js +4 -8
  51. package/dist/commands/explore.js.map +1 -1
  52. package/dist/commands/gate/gate.js +45 -8
  53. package/dist/commands/gate/gate.js.map +1 -1
  54. package/dist/commands/gate/print.js +15 -3
  55. package/dist/commands/gate/print.js.map +1 -1
  56. package/dist/commands/gherkin/commit.js +13 -54
  57. package/dist/commands/gherkin/commit.js.map +1 -1
  58. package/dist/commands/gherkin/gherkin.js +36 -20
  59. package/dist/commands/gherkin/gherkin.js.map +1 -1
  60. package/dist/commands/gherkin/reconcile.js +10 -13
  61. package/dist/commands/gherkin/reconcile.js.map +1 -1
  62. package/dist/commands/init/first-hour.js +44 -0
  63. package/dist/commands/init/first-hour.js.map +1 -1
  64. package/dist/commands/init/init.js +18 -22
  65. package/dist/commands/init/init.js.map +1 -1
  66. package/dist/commands/list/campaign/campaign.js +4 -6
  67. package/dist/commands/list/campaign/campaign.js.map +1 -1
  68. package/dist/commands/list/json.js +0 -4
  69. package/dist/commands/list/json.js.map +1 -1
  70. package/dist/commands/list/list.js +38 -23
  71. package/dist/commands/list/list.js.map +1 -1
  72. package/dist/commands/list/print.js +38 -6
  73. package/dist/commands/list/print.js.map +1 -1
  74. package/dist/commands/list/suites/suites.js +140 -0
  75. package/dist/commands/list/suites/suites.js.map +1 -0
  76. package/dist/commands/list/views.js +28 -33
  77. package/dist/commands/list/views.js.map +1 -1
  78. package/dist/commands/mcp/dispatch.js +91 -36
  79. package/dist/commands/mcp/dispatch.js.map +1 -1
  80. package/dist/commands/mcp/mcp.js +56 -15
  81. package/dist/commands/mcp/mcp.js.map +1 -1
  82. package/dist/commands/mcp/serve.js.map +1 -1
  83. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  84. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  85. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  86. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  87. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  88. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  89. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  90. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  91. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  92. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  93. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  94. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  95. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  96. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  97. package/dist/commands/new/capability-template.js +118 -0
  98. package/dist/commands/new/capability-template.js.map +1 -0
  99. package/dist/commands/new/commit.js +8 -22
  100. package/dist/commands/new/commit.js.map +1 -1
  101. package/dist/commands/new/modifies.js +206 -0
  102. package/dist/commands/new/modifies.js.map +1 -0
  103. package/dist/commands/new/new.js +59 -14
  104. package/dist/commands/new/new.js.map +1 -1
  105. package/dist/commands/new/templates.js +48 -124
  106. package/dist/commands/new/templates.js.map +1 -1
  107. package/dist/commands/open.js +3 -7
  108. package/dist/commands/open.js.map +1 -1
  109. package/dist/commands/policy/gate.js +27 -0
  110. package/dist/commands/policy/gate.js.map +1 -1
  111. package/dist/commands/rebase/plan.js +2 -2
  112. package/dist/commands/rebase/plan.js.map +1 -1
  113. package/dist/commands/rebase/rebase.js +60 -35
  114. package/dist/commands/rebase/rebase.js.map +1 -1
  115. package/dist/commands/seed/commit.js +21 -25
  116. package/dist/commands/seed/commit.js.map +1 -1
  117. package/dist/commands/seed/seed.js +6 -9
  118. package/dist/commands/seed/seed.js.map +1 -1
  119. package/dist/commands/show/show.js +3 -7
  120. package/dist/commands/show/show.js.map +1 -1
  121. package/dist/commands/status/print.js +13 -4
  122. package/dist/commands/status/print.js.map +1 -1
  123. package/dist/commands/status/status.js +3 -7
  124. package/dist/commands/status/status.js.map +1 -1
  125. package/dist/commands/steps/steps.js +3 -7
  126. package/dist/commands/steps/steps.js.map +1 -1
  127. package/dist/commands/subsystem/edit.js +3 -5
  128. package/dist/commands/subsystem/edit.js.map +1 -1
  129. package/dist/commands/subsystem/move.js +5 -8
  130. package/dist/commands/subsystem/move.js.map +1 -1
  131. package/dist/commands/subsystem/query.js +2 -4
  132. package/dist/commands/subsystem/query.js.map +1 -1
  133. package/dist/commands/subsystem/subsystem.js +4 -7
  134. package/dist/commands/subsystem/subsystem.js.map +1 -1
  135. package/dist/commands/subsystem/sync.js +14 -41
  136. package/dist/commands/subsystem/sync.js.map +1 -1
  137. package/dist/commands/subsystem/txn/txn.js +17 -38
  138. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  139. package/dist/commands/subsystem/txn/views.js +32 -5
  140. package/dist/commands/subsystem/txn/views.js.map +1 -1
  141. package/dist/commands/tasks/render.js +116 -0
  142. package/dist/commands/tasks/render.js.map +1 -0
  143. package/dist/commands/tasks/tasks.js +113 -0
  144. package/dist/commands/tasks/tasks.js.map +1 -0
  145. package/dist/commands/unarchive/manifest.js +6 -9
  146. package/dist/commands/unarchive/manifest.js.map +1 -1
  147. package/dist/commands/unarchive/restore.js +17 -28
  148. package/dist/commands/unarchive/restore.js.map +1 -1
  149. package/dist/commands/unarchive/unarchive.js +22 -30
  150. package/dist/commands/unarchive/unarchive.js.map +1 -1
  151. package/dist/commands/validate/arch-coverage.js +6 -15
  152. package/dist/commands/validate/arch-coverage.js.map +1 -1
  153. package/dist/commands/validate/checks/fleet-shape.js +4 -49
  154. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  155. package/dist/commands/validate/checks/requirements.js +18 -8
  156. package/dist/commands/validate/checks/requirements.js.map +1 -1
  157. package/dist/commands/validate/checks/sources.js +6 -2
  158. package/dist/commands/validate/checks/sources.js.map +1 -1
  159. package/dist/commands/validate/feature.js +23 -19
  160. package/dist/commands/validate/feature.js.map +1 -1
  161. package/dist/commands/validate/fleet/landscape.js +23 -23
  162. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  163. package/dist/commands/validate/fleet/map/attest.js +6 -10
  164. package/dist/commands/validate/fleet/map/attest.js.map +1 -1
  165. package/dist/commands/validate/fleet/map/bindings.js +1 -3
  166. package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
  167. package/dist/commands/validate/fleet/map/usecases.js +5 -11
  168. package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
  169. package/dist/commands/validate/fleet/obligations.js +1 -5
  170. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  171. package/dist/commands/validate/fleet/scorecard/print.js +5 -1
  172. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  173. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  174. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  175. package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
  176. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
  177. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
  178. package/dist/commands/validate/fleet/views/root-documents.js +8 -28
  179. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
  180. package/dist/commands/validate/links/corpus.js +1 -4
  181. package/dist/commands/validate/links/corpus.js.map +1 -1
  182. package/dist/commands/validate/links/glossary.js.map +1 -1
  183. package/dist/commands/validate/report.js +5 -4
  184. package/dist/commands/validate/report.js.map +1 -1
  185. package/dist/commands/validate/scope.js +113 -0
  186. package/dist/commands/validate/scope.js.map +1 -0
  187. package/dist/commands/validate/service/api.js +16 -104
  188. package/dist/commands/validate/service/api.js.map +1 -1
  189. package/dist/commands/validate/service/contracts/generated.js +134 -0
  190. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  191. package/dist/commands/validate/service/events/events.js +1 -1
  192. package/dist/commands/validate/service/events/events.js.map +1 -1
  193. package/dist/commands/validate/service/events/message-producers.js +1 -1
  194. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  195. package/dist/commands/validate/service/evidence-pins.js +56 -10
  196. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  197. package/dist/commands/validate/service/service.js +16 -26
  198. package/dist/commands/validate/service/service.js.map +1 -1
  199. package/dist/commands/validate/service/specs.js +31 -38
  200. package/dist/commands/validate/service/specs.js.map +1 -1
  201. package/dist/commands/validate/service/spine.js +8 -11
  202. package/dist/commands/validate/service/spine.js.map +1 -1
  203. package/dist/commands/validate/service/usecases/usecases.js +3 -3
  204. package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
  205. package/dist/commands/validate/validate.js +44 -26
  206. package/dist/commands/validate/validate.js.map +1 -1
  207. package/dist/commands/verify/cross/diff.js +2 -6
  208. package/dist/commands/verify/cross/diff.js.map +1 -1
  209. package/dist/commands/verify/evidence/contract.js +1 -1
  210. package/dist/commands/verify/evidence/contract.js.map +1 -1
  211. package/dist/commands/verify/evidence/read.js +12 -3
  212. package/dist/commands/verify/evidence/read.js.map +1 -1
  213. package/dist/commands/verify/frozen.js +45 -23
  214. package/dist/commands/verify/frozen.js.map +1 -1
  215. package/dist/commands/verify/record.js +69 -17
  216. package/dist/commands/verify/record.js.map +1 -1
  217. package/dist/commands/verify/report.js +27 -44
  218. package/dist/commands/verify/report.js.map +1 -1
  219. package/dist/commands/verify/results.js +112 -30
  220. package/dist/commands/verify/results.js.map +1 -1
  221. package/dist/commands/verify/verify.js +54 -51
  222. package/dist/commands/verify/verify.js.map +1 -1
  223. package/dist/commands/vouch/pack/pack.js +2 -2
  224. package/dist/commands/vouch/pack/pack.js.map +1 -1
  225. package/dist/commands/vouch/pack/print.js +1 -6
  226. package/dist/commands/vouch/pack/print.js.map +1 -1
  227. package/dist/commands/vouch/pack/sections.js +1 -2
  228. package/dist/commands/vouch/pack/sections.js.map +1 -1
  229. package/dist/commands/vouch/run.js +26 -81
  230. package/dist/commands/vouch/run.js.map +1 -1
  231. package/dist/commands/vouch/sample/plan.js +20 -34
  232. package/dist/commands/vouch/sample/plan.js.map +1 -1
  233. package/dist/commands/vouch/vet/verify.js +39 -7
  234. package/dist/commands/vouch/vet/verify.js.map +1 -1
  235. package/dist/commands/vouch/vouch.js +10 -17
  236. package/dist/commands/vouch/vouch.js.map +1 -1
  237. package/dist/core/agent/agents-md/command-map.js +56 -101
  238. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  239. package/dist/core/agent/agents-md/map/explain.js +4 -3
  240. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  241. package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
  242. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  243. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  244. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  245. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  246. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  247. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  248. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  249. package/dist/core/agent/agents-md/map/status.js +9 -3
  250. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  251. package/dist/core/agent/agents-md/map/subsystem.js +4 -3
  252. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  253. package/dist/core/agent/agents-md/map/usecases.js +24 -6
  254. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  255. package/dist/core/agent/agents-md/refusals.js +32 -18
  256. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  257. package/dist/core/agent/agents-md.js +9 -3
  258. package/dist/core/agent/agents-md.js.map +1 -1
  259. package/dist/core/agent/agents-stamp.js +10 -0
  260. package/dist/core/agent/agents-stamp.js.map +1 -1
  261. package/dist/core/agent/scaffold.js +10 -15
  262. package/dist/core/agent/scaffold.js.map +1 -1
  263. package/dist/core/agent/tools/dialects.js +1 -0
  264. package/dist/core/agent/tools/dialects.js.map +1 -1
  265. package/dist/core/agent/tools/registry.js +41 -76
  266. package/dist/core/agent/tools/registry.js.map +1 -1
  267. package/dist/core/agent/workflows/adopt.js +5 -3
  268. package/dist/core/agent/workflows/adopt.js.map +1 -1
  269. package/dist/core/agent/workflows/check.js +20 -16
  270. package/dist/core/agent/workflows/check.js.map +1 -1
  271. package/dist/core/agent/workflows/closing.js +54 -21
  272. package/dist/core/agent/workflows/closing.js.map +1 -1
  273. package/dist/core/agent/workflows/feature.js +79 -11
  274. package/dist/core/agent/workflows/feature.js.map +1 -1
  275. package/dist/core/agent/workflows/implement.js +91 -13
  276. package/dist/core/agent/workflows/implement.js.map +1 -1
  277. package/dist/core/agent/workflows/reference/authoring.js +3 -1
  278. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  279. package/dist/core/agent/workflows/reference/codes.js +8 -6
  280. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  281. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  282. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  283. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  284. package/dist/core/agent/workflows/reference/spine.js +50 -18
  285. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  286. package/dist/core/asyncapi/baseline/plan.js +14 -81
  287. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  288. package/dist/core/asyncapi/digest.js +30 -25
  289. package/dist/core/asyncapi/digest.js.map +1 -1
  290. package/dist/core/asyncapi/merge/components.js +5 -4
  291. package/dist/core/asyncapi/merge/components.js.map +1 -1
  292. package/dist/core/asyncapi/merge/error.js +7 -2
  293. package/dist/core/asyncapi/merge/error.js.map +1 -1
  294. package/dist/core/asyncapi/merge/markers.js +30 -50
  295. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  296. package/dist/core/asyncapi/merge/merge.js +31 -53
  297. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  298. package/dist/core/asyncapi/merge/pin.js +10 -36
  299. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  300. package/dist/core/asyncapi/model.js.map +1 -1
  301. package/dist/core/asyncapi/read.js +36 -12
  302. package/dist/core/asyncapi/read.js.map +1 -1
  303. package/dist/core/brief/brief.js +6 -18
  304. package/dist/core/brief/brief.js.map +1 -1
  305. package/dist/core/brief/landscape.js +8 -12
  306. package/dist/core/brief/landscape.js.map +1 -1
  307. package/dist/core/brief/map/extend.js +8 -5
  308. package/dist/core/brief/map/extend.js.map +1 -1
  309. package/dist/core/brief/map/owed.js +8 -26
  310. package/dist/core/brief/map/owed.js.map +1 -1
  311. package/dist/core/brief/targets.js +15 -5
  312. package/dist/core/brief/targets.js.map +1 -1
  313. package/dist/core/c4/likec4.js +147 -16
  314. package/dist/core/c4/likec4.js.map +1 -1
  315. package/dist/core/c4/locate/unlocated.js +150 -0
  316. package/dist/core/c4/locate/unlocated.js.map +1 -0
  317. package/dist/core/c4/parsed/values.js +54 -1
  318. package/dist/core/c4/parsed/values.js.map +1 -1
  319. package/dist/core/c4/prefetch/fleet.js +1 -9
  320. package/dist/core/c4/prefetch/fleet.js.map +1 -1
  321. package/dist/core/c4/project/documents.js +31 -6
  322. package/dist/core/c4/project/documents.js.map +1 -1
  323. package/dist/core/c4/project/load.js +6 -17
  324. package/dist/core/c4/project/load.js.map +1 -1
  325. package/dist/core/c4/resolve/external.js +51 -0
  326. package/dist/core/c4/resolve/external.js.map +1 -0
  327. package/dist/core/c4/resolve/service.js +25 -1
  328. package/dist/core/c4/resolve/service.js.map +1 -1
  329. package/dist/core/c4/seed/template.js +15 -1
  330. package/dist/core/c4/seed/template.js.map +1 -1
  331. package/dist/core/c4/service-model/shape.js +2 -22
  332. package/dist/core/c4/service-model/shape.js.map +1 -1
  333. package/dist/core/c4/source-mask.js +288 -4
  334. package/dist/core/c4/source-mask.js.map +1 -1
  335. package/dist/core/c4/source-scan.js +203 -147
  336. package/dist/core/c4/source-scan.js.map +1 -1
  337. package/dist/core/c4/splice/authored-source.js +19 -0
  338. package/dist/core/c4/splice/authored-source.js.map +1 -1
  339. package/dist/core/c4/splice/delta-blocks.js +5 -18
  340. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  341. package/dist/core/c4/splice/landscape-merge.js +13 -8
  342. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  343. package/dist/core/c4/splice/model/merge.js +10 -6
  344. package/dist/core/c4/splice/model/merge.js.map +1 -1
  345. package/dist/core/c4/splice/placement.js +108 -18
  346. package/dist/core/c4/splice/placement.js.map +1 -1
  347. package/dist/core/c4/workspace.js +13 -22
  348. package/dist/core/c4/workspace.js.map +1 -1
  349. package/dist/core/capabilities/capabilities.js +6 -6
  350. package/dist/core/capabilities/capabilities.js.map +1 -1
  351. package/dist/core/capabilities/delta/doc.js +7 -10
  352. package/dist/core/capabilities/delta/doc.js.map +1 -1
  353. package/dist/core/capabilities/findings.js +15 -20
  354. package/dist/core/capabilities/findings.js.map +1 -1
  355. package/dist/core/capabilities/realizes/findings.js +12 -19
  356. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  357. package/dist/core/capabilities/realizes/join.js +0 -20
  358. package/dist/core/capabilities/realizes/join.js.map +1 -1
  359. package/dist/core/capabilities/rollup.js +6 -0
  360. package/dist/core/capabilities/rollup.js.map +1 -1
  361. package/dist/core/capabilities/tree.js +6 -28
  362. package/dist/core/capabilities/tree.js.map +1 -1
  363. package/dist/core/capabilities/usecase-join.js +3 -13
  364. package/dist/core/capabilities/usecase-join.js.map +1 -1
  365. package/dist/core/coherence/authoring/scaffold.js +11 -3
  366. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  367. package/dist/core/coherence/authoring/sentinels.js +2 -1
  368. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  369. package/dist/core/coherence/coherence.js +30 -16
  370. package/dist/core/coherence/coherence.js.map +1 -1
  371. package/dist/core/coherence/consumed/operations.js +70 -0
  372. package/dist/core/coherence/consumed/operations.js.map +1 -0
  373. package/dist/core/coherence/consumed/refusal.js +38 -0
  374. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  375. package/dist/core/coherence/declared.js +60 -34
  376. package/dist/core/coherence/declared.js.map +1 -1
  377. package/dist/core/coherence/events/declared.js +44 -59
  378. package/dist/core/coherence/events/declared.js.map +1 -1
  379. package/dist/core/coherence/events/events.js +119 -8
  380. package/dist/core/coherence/events/events.js.map +1 -1
  381. package/dist/core/coherence/events/grades.js +31 -8
  382. package/dist/core/coherence/events/grades.js.map +1 -1
  383. package/dist/core/coherence/events/lookups.js +14 -82
  384. package/dist/core/coherence/events/lookups.js.map +1 -1
  385. package/dist/core/coherence/lookups.js +18 -78
  386. package/dist/core/coherence/lookups.js.map +1 -1
  387. package/dist/core/coherence/retired/archived.js +285 -0
  388. package/dist/core/coherence/retired/archived.js.map +1 -0
  389. package/dist/core/coherence/retired/event-pair.js +62 -0
  390. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  391. package/dist/core/coherence/retired/merged.js +79 -0
  392. package/dist/core/coherence/retired/merged.js.map +1 -0
  393. package/dist/core/coherence/retired/retirement.js +49 -0
  394. package/dist/core/coherence/retired/retirement.js.map +1 -0
  395. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  396. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  397. package/dist/core/coherence/retired/revival/revived.js +297 -0
  398. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  399. package/dist/core/consumers/after.js +113 -0
  400. package/dist/core/consumers/after.js.map +1 -0
  401. package/dist/core/consumers/consumers.js +296 -0
  402. package/dist/core/consumers/consumers.js.map +1 -0
  403. package/dist/core/consumers/current.js +211 -0
  404. package/dist/core/consumers/current.js.map +1 -0
  405. package/dist/core/consumers/scan.js +46 -0
  406. package/dist/core/consumers/scan.js.map +1 -0
  407. package/dist/core/consumers/state.js +27 -0
  408. package/dist/core/consumers/state.js.map +1 -0
  409. package/dist/core/delta/delta.js +1 -1
  410. package/dist/core/delta/delta.js.map +1 -1
  411. package/dist/core/deployment/delta.js +3 -6
  412. package/dist/core/deployment/delta.js.map +1 -1
  413. package/dist/core/diff/base-git.js +94 -12
  414. package/dist/core/diff/base-git.js.map +1 -1
  415. package/dist/core/diff/base-state.js +28 -16
  416. package/dist/core/diff/base-state.js.map +1 -1
  417. package/dist/core/diff/joins.js +136 -0
  418. package/dist/core/diff/joins.js.map +1 -0
  419. package/dist/core/diff/scope/changed-paths.js +22 -6
  420. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  421. package/dist/core/diff/scope/edges.js +82 -0
  422. package/dist/core/diff/scope/edges.js.map +1 -0
  423. package/dist/core/diff/semantic.js +100 -31
  424. package/dist/core/diff/semantic.js.map +1 -1
  425. package/dist/core/doctor/agents.js +34 -18
  426. package/dist/core/doctor/agents.js.map +1 -1
  427. package/dist/core/doctor/report.js.map +1 -1
  428. package/dist/core/document/apply.js +10 -2
  429. package/dist/core/document/apply.js.map +1 -1
  430. package/dist/core/document/frontmatter.js +55 -0
  431. package/dist/core/document/frontmatter.js.map +1 -1
  432. package/dist/core/document/parse.js +29 -6
  433. package/dist/core/document/parse.js.map +1 -1
  434. package/dist/core/document/scenarios.js +17 -0
  435. package/dist/core/document/scenarios.js.map +1 -1
  436. package/dist/core/document/spec.js +4 -11
  437. package/dist/core/document/spec.js.map +1 -1
  438. package/dist/core/envelope/config.js +10 -0
  439. package/dist/core/envelope/config.js.map +1 -1
  440. package/dist/core/envelope/json.js +34 -0
  441. package/dist/core/envelope/json.js.map +1 -1
  442. package/dist/core/explain/families.js +17 -12
  443. package/dist/core/explain/families.js.map +1 -1
  444. package/dist/core/explain/refusals.js +3 -3
  445. package/dist/core/explain/refusals.js.map +1 -1
  446. package/dist/core/explain/terms.js +18 -9
  447. package/dist/core/explain/terms.js.map +1 -1
  448. package/dist/core/explore/explore.js +2 -7
  449. package/dist/core/explore/explore.js.map +1 -1
  450. package/dist/core/fleet-context.js +34 -95
  451. package/dist/core/fleet-context.js.map +1 -1
  452. package/dist/core/gate/checks.js +5 -2
  453. package/dist/core/gate/checks.js.map +1 -1
  454. package/dist/core/gate/partners.js +4 -2
  455. package/dist/core/gate/partners.js.map +1 -1
  456. package/dist/core/gate/report.js.map +1 -1
  457. package/dist/core/gate/verification.js +75 -12
  458. package/dist/core/gate/verification.js.map +1 -1
  459. package/dist/core/gherkin/stale.js +13 -37
  460. package/dist/core/gherkin/stale.js.map +1 -1
  461. package/dist/core/glossary/tree.js +6 -19
  462. package/dist/core/glossary/tree.js.map +1 -1
  463. package/dist/core/kernel/concurrency.js +19 -0
  464. package/dist/core/kernel/concurrency.js.map +1 -1
  465. package/dist/core/kernel/records.js +11 -0
  466. package/dist/core/kernel/records.js.map +1 -1
  467. package/dist/core/links/backlinks.js +1 -3
  468. package/dist/core/links/backlinks.js.map +1 -1
  469. package/dist/core/links/corpus.js +5 -36
  470. package/dist/core/links/corpus.js.map +1 -1
  471. package/dist/core/mcp/argv.js +101 -0
  472. package/dist/core/mcp/argv.js.map +1 -1
  473. package/dist/core/mcp/author-tools.js +2 -1
  474. package/dist/core/mcp/author-tools.js.map +1 -1
  475. package/dist/core/mcp/protocol.js +2 -2
  476. package/dist/core/mcp/protocol.js.map +1 -1
  477. package/dist/core/mcp/tools.js +43 -33
  478. package/dist/core/mcp/tools.js.map +1 -1
  479. package/dist/core/openapi/baseline/gate.js +53 -12
  480. package/dist/core/openapi/baseline/gate.js.map +1 -1
  481. package/dist/core/openapi/baseline/plan.js +22 -8
  482. package/dist/core/openapi/baseline/plan.js.map +1 -1
  483. package/dist/core/openapi/baseline/record.js +0 -1
  484. package/dist/core/openapi/baseline/record.js.map +1 -1
  485. package/dist/core/openapi/contract-text.js +33 -0
  486. package/dist/core/openapi/contract-text.js.map +1 -0
  487. package/dist/core/openapi/contract-tree.js +56 -0
  488. package/dist/core/openapi/contract-tree.js.map +1 -0
  489. package/dist/core/openapi/depth.js +38 -24
  490. package/dist/core/openapi/depth.js.map +1 -1
  491. package/dist/core/openapi/digest.js +52 -2
  492. package/dist/core/openapi/digest.js.map +1 -1
  493. package/dist/core/openapi/doc.js +12 -4
  494. package/dist/core/openapi/doc.js.map +1 -1
  495. package/dist/core/openapi/merge/components.js +25 -60
  496. package/dist/core/openapi/merge/components.js.map +1 -1
  497. package/dist/core/openapi/merge/error.js +3 -13
  498. package/dist/core/openapi/merge/error.js.map +1 -1
  499. package/dist/core/openapi/merge/markers.js +26 -30
  500. package/dist/core/openapi/merge/markers.js.map +1 -1
  501. package/dist/core/openapi/merge/merge.js +26 -52
  502. package/dist/core/openapi/merge/merge.js.map +1 -1
  503. package/dist/core/openapi/merge/pin.js +15 -35
  504. package/dist/core/openapi/merge/pin.js.map +1 -1
  505. package/dist/core/openspec/decide/verdict.js.map +1 -1
  506. package/dist/core/openspec/inventory.js +4 -5
  507. package/dist/core/openspec/inventory.js.map +1 -1
  508. package/dist/core/openspec/model/mapping.js +30 -28
  509. package/dist/core/openspec/model/mapping.js.map +1 -1
  510. package/dist/core/openspec/model/model.js +12 -7
  511. package/dist/core/openspec/model/model.js.map +1 -1
  512. package/dist/core/openspec/scan/changes.js +10 -4
  513. package/dist/core/openspec/scan/changes.js.map +1 -1
  514. package/dist/core/openspec/scan/config.js +7 -16
  515. package/dist/core/openspec/scan/config.js.map +1 -1
  516. package/dist/core/openspec/scan/shape.js +63 -19
  517. package/dist/core/openspec/scan/shape.js.map +1 -1
  518. package/dist/core/openspec/scan/walk.js +21 -38
  519. package/dist/core/openspec/scan/walk.js.map +1 -1
  520. package/dist/core/pack/living.js +2 -16
  521. package/dist/core/pack/living.js.map +1 -1
  522. package/dist/core/provenance/findings.js +48 -12
  523. package/dist/core/provenance/findings.js.map +1 -1
  524. package/dist/core/provenance/git.js +33 -57
  525. package/dist/core/provenance/git.js.map +1 -1
  526. package/dist/core/provenance/gitq/moves.js +71 -114
  527. package/dist/core/provenance/gitq/moves.js.map +1 -1
  528. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  529. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  530. package/dist/core/provenance/sources.js +23 -6
  531. package/dist/core/provenance/sources.js.map +1 -1
  532. package/dist/core/provenance/stamp.js +5 -17
  533. package/dist/core/provenance/stamp.js.map +1 -1
  534. package/dist/core/repo/entries.js.map +1 -1
  535. package/dist/core/repo/repo.js +24 -14
  536. package/dist/core/repo/repo.js.map +1 -1
  537. package/dist/core/repo/tree/find.js +12 -3
  538. package/dist/core/repo/tree/find.js.map +1 -1
  539. package/dist/core/repo/tree/fs.js +27 -1
  540. package/dist/core/repo/tree/fs.js.map +1 -1
  541. package/dist/core/repo/tree/walk.js +8 -30
  542. package/dist/core/repo/tree/walk.js.map +1 -1
  543. package/dist/core/results.js +47 -12
  544. package/dist/core/results.js.map +1 -1
  545. package/dist/core/staging/commit.js +10 -12
  546. package/dist/core/staging/commit.js.map +1 -1
  547. package/dist/core/staging/lock.js +74 -12
  548. package/dist/core/staging/lock.js.map +1 -1
  549. package/dist/core/staging/recovery/intent.js +2 -5
  550. package/dist/core/staging/recovery/intent.js.map +1 -1
  551. package/dist/core/staging/recovery/recover.js +60 -1
  552. package/dist/core/staging/recovery/recover.js.map +1 -1
  553. package/dist/core/staging/snapshot/manifest.js +152 -0
  554. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  555. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
  556. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  557. package/dist/core/status/actions/execution.js +11 -0
  558. package/dist/core/status/actions/execution.js.map +1 -1
  559. package/dist/core/status/feature/artifacts.js +2 -2
  560. package/dist/core/status/feature/artifacts.js.map +1 -1
  561. package/dist/core/status/feature/feature.js +5 -11
  562. package/dist/core/status/feature/feature.js.map +1 -1
  563. package/dist/core/status/fleet/fleet.js +6 -2
  564. package/dist/core/status/fleet/fleet.js.map +1 -1
  565. package/dist/core/status/fleet/next.js +26 -3
  566. package/dist/core/status/fleet/next.js.map +1 -1
  567. package/dist/core/status/report.js.map +1 -1
  568. package/dist/core/status/verification.js +30 -15
  569. package/dist/core/status/verification.js.map +1 -1
  570. package/dist/core/tasks/packet.js +174 -0
  571. package/dist/core/tasks/packet.js.map +1 -0
  572. package/dist/core/tasks/record.js +43 -0
  573. package/dist/core/tasks/record.js.map +1 -0
  574. package/dist/core/tasks/sources.js +181 -0
  575. package/dist/core/tasks/sources.js.map +1 -0
  576. package/dist/core/tasks/stamps.js +76 -0
  577. package/dist/core/tasks/stamps.js.map +1 -0
  578. package/dist/core/tasks/unit.js +0 -0
  579. package/dist/core/tasks/unit.js.map +1 -0
  580. package/dist/core/usecases/capability.js +11 -14
  581. package/dist/core/usecases/capability.js.map +1 -1
  582. package/dist/core/usecases/delta/flows.js +5 -7
  583. package/dist/core/usecases/delta/flows.js.map +1 -1
  584. package/dist/core/usecases/fleet.js +178 -31
  585. package/dist/core/usecases/fleet.js.map +1 -1
  586. package/dist/core/usecases/operations.js +5 -4
  587. package/dist/core/usecases/operations.js.map +1 -1
  588. package/dist/core/usecases/service/flows.js +9 -6
  589. package/dist/core/usecases/service/flows.js.map +1 -1
  590. package/dist/core/usecases/suites/participants.js +83 -0
  591. package/dist/core/usecases/suites/participants.js.map +1 -0
  592. package/dist/core/usecases/suites/suites.js +45 -0
  593. package/dist/core/usecases/suites/suites.js.map +1 -0
  594. package/dist/core/verify/answers.js +12 -5
  595. package/dist/core/verify/answers.js.map +1 -1
  596. package/dist/core/verify/build.js +102 -82
  597. package/dist/core/verify/build.js.map +1 -1
  598. package/dist/core/verify/checklist.js +3 -0
  599. package/dist/core/verify/checklist.js.map +1 -1
  600. package/dist/core/verify/location.js +67 -0
  601. package/dist/core/verify/location.js.map +1 -0
  602. package/dist/core/verify/pins/lint.js +3 -2
  603. package/dist/core/verify/pins/lint.js.map +1 -1
  604. package/dist/core/verify/pins/pin.js +1 -1
  605. package/dist/core/verify/pins/pin.js.map +1 -1
  606. package/dist/core/verify/read/file.js +224 -0
  607. package/dist/core/verify/read/file.js.map +1 -0
  608. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  609. package/dist/core/verify/read/shape.js.map +1 -0
  610. package/dist/core/verify/read/union.js +105 -0
  611. package/dist/core/verify/read/union.js.map +1 -0
  612. package/dist/core/verify/record.js +35 -12
  613. package/dist/core/verify/record.js.map +1 -1
  614. package/dist/core/verify/store/commit.js +40 -30
  615. package/dist/core/verify/store/commit.js.map +1 -1
  616. package/dist/core/verify/store/render.js +8 -4
  617. package/dist/core/verify/store/render.js.map +1 -1
  618. package/dist/core/vocabulary/issue.js +1 -0
  619. package/dist/core/vocabulary/issue.js.map +1 -1
  620. package/dist/core/vocabulary/maturity.js +12 -2
  621. package/dist/core/vocabulary/maturity.js.map +1 -1
  622. package/examples/README.md +21 -6
  623. package/examples/docs/architecture/landscape.likec4 +8 -0
  624. package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
  625. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  626. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  627. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  628. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  629. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  630. package/package.json +1 -1
  631. package/dist/commands/validate/checks/vocabulary.js +0 -16
  632. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  633. package/dist/core/agent/agents-md/artifacts.js +0 -221
  634. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  635. package/dist/core/agent/agents-md/cycle.js +0 -180
  636. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  637. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  638. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  639. package/dist/core/agent/agents-md/spine.js +0 -40
  640. package/dist/core/agent/agents-md/spine.js.map +0 -1
  641. package/dist/core/coherence/events/merged.js +0 -40
  642. package/dist/core/coherence/events/merged.js.map +0 -1
  643. package/dist/core/diff/current-state.js +0 -96
  644. package/dist/core/diff/current-state.js.map +0 -1
  645. package/dist/core/diff/victims.js +0 -256
  646. package/dist/core/diff/victims.js.map +0 -1
  647. package/dist/core/staging/snapshot.js.map +0 -1
  648. package/dist/core/verify/file.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"usecases.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/usecases/usecases.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The use-case axis of the SERVICE target: the `dynamic view`s a service's own\n * LikeC4 project declares, graded as the hop sequences behind its requirements.\n *\n * The fleet target grades flows drawn over the map (`../../fleet/usecases/`);\n * this grades flows drawn over ONE service's containers, which only that\n * service's project can resolve — the whole reason the slot exists\n * (`core/usecases/service/flows.ts` carries the report). The step grades are\n * the fleet's own `stepFindings`, reused with the service's project as the\n * model, the enumerated fleet as `services`, and one injection: where a\n * finding says the view is. A second verdict→code mapping here would be the\n * drift that package's banner forbids.\n *\n * ONLY VIEWS CARRYING A RESERVED TAG ARE GRADED, as at fleet altitude and for\n * the same reason: a service's `views.likec4` full of hand-drawn diagrams must\n * not turn red on upgrade. The reader applies the opt-in; this module never\n * sees an untagged view. Graded is narrower than LOADED, and the difference is\n * the reader's gate (`core/usecases/service/flows.ts`): an untagged sibling is\n * not even loaded while no view in the project opts in and no sibling mentions\n * either reserved prefix anywhere in its bytes — but once a view in\n * model.likec4 itself opts in, or a sibling mentions `req-` or `cap-` in a\n * comment or a title, every sibling is staged, and a project that does not\n * read is one `usecase.flow-invalid` whether or not any view in it opted in.\n *\n * A SIBLING THAT BREAKS THE PROJECT is ONE `usecase.flow-invalid` — reused,\n * because its meaning is exactly this (\"loam could not read the flows against\n * the map they are drawn over, so it graded none of them, and the renderer\n * refuses the same project\") applied to the service project instead of the\n * merge preview — and NEVER `c4.invalid`: that code means \"model.likec4 does\n * not parse, every model check is suspended\", and here the model parses and\n * every model check RAN. Reusing it would tell an agent to fix the wrong file\n * and tell the brief's `checks[]` row (\"every other check stops here\") a lie.\n */\nimport { existsSync } from \"node:fs\";\nimport { relative } from \"node:path\";\nimport type { LikeC4Error } from \"../../../../core/c4/likec4.js\";\nimport type { Requirement } from \"../../../../core/document/spec.js\";\nimport type { ServicePaths } from \"../../../../core/repo/paths.js\";\nimport type { ParsedView } from \"../../../../core/c4/parsed/dynamic-views.js\";\nimport { serviceResolver } from \"../../../../core/c4/resolve/service.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\nimport { compareIds } from \"../../../../core/repo/entries.js\";\nimport type { ServiceFlowScan } from \"../../../../core/usecases/service/flows.js\";\nimport type { Finding } from \"../../../../core/vocabulary/report.js\";\nimport { errorText } from \"../../../../core/c4/likec4.js\";\nimport { CAP_TAG_PREFIX, REQ_TAG_PREFIX } from \"../../../../core/capabilities/usecase-join.js\";\nimport { stepFindings, type StepGrading } from \"../../fleet/usecases/steps.js\";\nimport { serviceTagFindings, type ServiceTagScope } from \"./tags.js\";\n\n/** What the service's use-case grades run over. */\nexport interface ServiceUseCaseScope extends ServiceTagScope {\n /** The docs root, so a project error's absolute path can be spelled repo-relative. */\n docsDir: DocsDir;\n scan: ServiceFlowScan;\n /**\n * The `services/<id>/` directories that exist — the SAME enumerated set the\n * fleet target hands `stepFindings`, never an empty one: `StepGrading`\n * documents why the intra-service exemption is a guard and not a scope.\n */\n services: ReadonlySet<string>;\n}\n\n/** `<svc>: services/<tree>/<file> — dynamic view '<id>'` — the service target's spelling of `viewPlace`. */\nfunction servicePlace(scope: ServiceUseCaseScope, view: ParsedView): string {\n // An absent `sourcePath` names the directory rather than guessing a file,\n // for `viewFile`'s reason; nothing at the 1.59.2 pin reaches that arm.\n return `${scope.service}: ${scope.treePath}/${view.sourcePath ?? \"\"} — dynamic view '${view.id}'`;\n}\n\n/** The repo-relative spelling of a project error's document, or the whole project when it carries none. */\nfunction fileOf(scope: ServiceUseCaseScope, error: LikeC4Error): string {\n return error.sourceFsPath === undefined\n ? `${scope.treePath}/`\n : relative(scope.docsDir, error.sourceFsPath).split(/[\\\\/]/).join(\"/\");\n}\n\n/**\n * The one finding a broken project earns. `details` is one `errorText` line\n * per error, each prefixed by its file, because the project may hold several\n * siblings and the fault may sit in any of them — including `model.likec4`\n * itself when a sibling re-declares its `specification` block, an error LikeC4\n * blames on both files. `locations` names the first broken file, in the sorted\n * order the reader produced.\n */\nfunction flowInvalidFinding(scope: ServiceUseCaseScope, errors: readonly LikeC4Error[]): Finding {\n const files = [...new Set(errors.map((error) => fileOf(scope, error)))];\n const [first = `${scope.treePath}/`, ...others] = files;\n const named = others.length === 0 ? first : `${first} and ${others.length} more file(s)`;\n return {\n severity: \"error\",\n code: \"usecase.flow-invalid\",\n subject: scope.service,\n message:\n `${scope.service}: ${named} has ${errors.length} error(s) — every .likec4 beside model.likec4 is one LikeC4 ` +\n \"project, read the way the renderer reads it, and the renderer refuses the whole project over one broken \" +\n \"file; no flow in it was graded. model.likec4 itself parses and is graded alone.\",\n // The hint first, for `c4.invalid`'s reason (`../model/grade.ts`):\n // `capDetails` keeps ten lines, one broken document can carry more than ten\n // errors, and a diagnosis appended after them is dropped exactly when the\n // reader needs it.\n details: [...tagHint(errors), ...errors.map((error) => `${fileOf(scope, error)}: ${errorText(error)}`)],\n locations: [{ path: first, role: \"primary\" }],\n };\n}\n\n/** `Could not resolve reference to Tag named '#req-PAY-AUTH'.` — measured at the 1.59.2 pin. */\nconst UNDECLARED_TAG = /reference to Tag named '#?([\\w.-]+)'/;\n\n/**\n * The one line an undeclared RESERVED tag earns.\n *\n * The opt-in is a tag, LikeC4 refuses a tag no `specification` declares, and the\n * refusal is a bare `Could not resolve reference to Tag named …` — so an author\n * following the protocol's \"tag the view `#req-<id>`\" got a parse error naming\n * the thing they had just been told to write, with nothing saying a tag must be\n * declared first (verification 2026-09-04, D10). One line, from the first such\n * error only: the second one has the same repair.\n */\nfunction tagHint(errors: readonly LikeC4Error[]): string[] {\n for (const error of errors) {\n const match = UNDECLARED_TAG.exec(error.message);\n const name = match?.[1];\n if (name === undefined) continue;\n // `cap-` and `req-` do NOT share a repair here, and covering both with one\n // hint sent an author two steps where one would do: declaring `tag cap-…`\n // makes the file parse and the very next run says `usecase.capability-\n // unresolved` — \"no capability can be claimed inside this service's own\n // project. Drop the tag.\" A capability is claimed at fleet altitude, so the\n // tag can never resolve to anything beside a model, whatever is declared\n // (verification 2026-09-04).\n if (name.startsWith(CAP_TAG_PREFIX)) {\n return [\n `\\`#${name}\\` claims a capability, and a capability is claimed at FLEET altitude only — a flow beside ` +\n \"model.likec4 is read in this service's own project, where no capability can be resolved. Declaring \" +\n `\\`tag ${name}\\` makes the file parse and earns \\`usecase.capability-unresolved\\` on the next run: ` +\n `drop the tag, and tag the flow \\`#${REQ_TAG_PREFIX}<Requirement-ID>\\` instead, or draw it in ` +\n \"`architecture/usecases/` where a capability tag belongs\",\n ];\n }\n if (!name.startsWith(REQ_TAG_PREFIX)) continue;\n return [\n `declare \\`tag ${name}\\` in the \\`specification { }\\` block this project reads — model.likec4's own ` +\n \"for a model that stands alone, the fleet map's for one that extends it (or a tags-only \" +\n \"`specification` in a document beside the model)\",\n ];\n }\n return [];\n}\n\n/**\n * The FILE each finding is about, as `locations[0]`.\n *\n * The step and tag grades carry no `locations` of their own, so `findingJson`\n * fell back to the target's scope — `services/<tree>` and nothing else — and an\n * agent reading `--json`, which is the reason the envelope exists, had to parse\n * the prose to learn which `.likec4` to open. `usecase.flow-invalid` on this\n * same arm already names its file; these say the file the same way, with the\n * service directory kept beside it as `scope` (`../spine.ts`'s shape).\n */\nfunction atView(scope: ServiceUseCaseScope, view: ParsedView, findings: Finding[]): Finding[] {\n const file = view.sourcePath === undefined ? `${scope.treePath}/` : `${scope.treePath}/${view.sourcePath}`;\n return findings.map((finding) =>\n finding.locations === undefined\n ? {\n ...finding,\n locations: [\n { path: file, role: \"primary\" as const },\n { path: scope.treePath, role: \"scope\" as const },\n ],\n }\n : finding,\n );\n}\n\n/**\n * Every use-case finding a service's own project earns.\n *\n * Views are sorted by (file, view id) for the fleet grader's reason: nothing in\n * loam has measured that the parse preserves declaration order, and a report\n * whose row order depends on it would reorder under a dependency bump.\n */\nexport function serviceUseCaseFindings(scope: ServiceUseCaseScope): Finding[] {\n const { scan } = scope;\n if (scan.kind === \"none\") return [];\n if (scan.kind === \"unreadable\") return [flowInvalidFinding(scope, scan.errors)];\n\n const place = (view: ParsedView): string => servicePlace(scope, view);\n const grading: StepGrading = {\n model: scan.model,\n services: scope.services,\n // Built over the PROJECT's elements with the fleet set, exactly as the\n // attribution's own fallback tier resolves — so \"which service is this\n // container\" cannot be answered one way by `attributeStep` and another by\n // the provider guard.\n resolve: serviceResolver(scan.model.elements, scan.model.known),\n place,\n };\n const findings: Finding[] = [];\n const views = [...scan.views].sort(\n (a, b) => compareIds(a.sourcePath ?? \"\", b.sourcePath ?? \"\") || compareIds(a.id, b.id),\n );\n for (const view of views) {\n const forView: Finding[] = [...serviceTagFindings(view, scope, place(view))];\n for (const step of view.steps) forView.push(...stepFindings({ view, step }, grading));\n findings.push(...atView(scope, view, forView));\n }\n return findings;\n}\n\n/**\n * The `Requirement-ID`s a service-local `#req-` tag may name: every identified\n * requirement of spec.md (living — a REMOVED one is on its way out and keeps no\n * promise) and of arch.spec.md, as ONE set. `undefined` when NEITHER document\n * exists, which the tag grade reads as \"no requirement to satisfy\" rather than\n * \"none flattening to this slug\" — the same two answers the fleet's\n * `requirementsOf` gives for a capability with no document at all.\n *\n * An id both documents declare resolves once (a set dedupes it); two ids that\n * merely flatten alike — `PAY.AUTH` in one file, `PAY-AUTH` in the other — are\n * the `many` arm, because a Requirement-ID is unique inside one document and a\n * flow beside the model has two.\n *\n * It lives beside the grade it feeds rather than in `../service.ts`, which is\n * the ORDER and nothing else: the set is this axis's input, its two answers are\n * this axis's vocabulary, and the order module had grown past its line limit\n * carrying it.\n */\nexport function requirementIdsOf(\n paths: ServicePaths,\n livingReqs: readonly Requirement[],\n archReqs: readonly Requirement[],\n): ReadonlySet<string> | undefined {\n if (!existsSync(paths.spec) && !existsSync(paths.archSpec)) return undefined;\n const ids = new Set<string>();\n for (const req of [...livingReqs, ...archReqs.filter((r) => r.kind !== \"REMOVED\")]) {\n if (req.id !== undefined) ids.add(req.id);\n }\n return ids;\n}\n"]}
1
+ {"version":3,"file":"usecases.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/usecases/usecases.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The use-case axis of the SERVICE target: the `dynamic view`s a service's own\n * LikeC4 project declares, graded as the hop sequences behind its requirements.\n *\n * The fleet target grades flows drawn over the map (`../../fleet/usecases/`);\n * this grades flows drawn over ONE service's containers, which only that\n * service's project can resolve — the whole reason the slot exists\n * (`core/usecases/service/flows.ts` carries the report). The step grades are\n * the fleet's own `stepFindings`, reused with the service's project as the\n * model, the enumerated fleet as `services`, and one injection: where a\n * finding says the view is. A second verdict→code mapping here would be the\n * drift that package's banner forbids.\n *\n * ONLY VIEWS CARRYING A RESERVED TAG ARE GRADED, as at fleet altitude and for\n * the same reason: a service's `views.likec4` full of hand-drawn diagrams must\n * not turn red on upgrade. The reader applies the opt-in; this module never\n * sees an untagged view. Graded is narrower than LOADED, and the difference is\n * the reader's gate (`core/usecases/service/flows.ts`): an untagged sibling is\n * not even loaded while no view in the project opts in and no sibling mentions\n * a reserved prefix anywhere in its bytes — but once a view in\n * model.likec4 itself opts in, or a sibling mentions `req-`, `cap-` or `suite-`\n * in a comment or a title, every sibling is staged, and a project that does not\n * read is one `usecase.flow-invalid` whether or not any view in it opted in.\n *\n * A SIBLING THAT BREAKS THE PROJECT is ONE `usecase.flow-invalid` — reused,\n * because its meaning is exactly this (\"loam could not read the flows against\n * the map they are drawn over, so it graded none of them, and the renderer\n * refuses the same project\") applied to the service project instead of the\n * merge preview — and NEVER `c4.invalid`: that code means \"model.likec4 does\n * not parse, every model check is suspended\", and here the model parses and\n * every model check RAN. Reusing it would tell an agent to fix the wrong file\n * and tell the brief's `checks[]` row (\"every other check stops here\") a lie.\n */\nimport { existsSync } from \"node:fs\";\nimport { relative } from \"node:path\";\nimport type { LikeC4Error } from \"../../../../core/c4/likec4.js\";\nimport type { Requirement } from \"../../../../core/document/spec.js\";\nimport type { ServicePaths } from \"../../../../core/repo/paths.js\";\nimport type { ParsedView } from \"../../../../core/c4/parsed/dynamic-views.js\";\nimport { serviceResolver } from \"../../../../core/c4/resolve/service.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\nimport { compareIds } from \"../../../../core/repo/entries.js\";\nimport type { ServiceFlowScan } from \"../../../../core/usecases/service/flows.js\";\nimport type { Finding } from \"../../../../core/vocabulary/report.js\";\nimport { errorText } from \"../../../../core/c4/likec4.js\";\nimport { CAP_TAG_PREFIX, REQ_TAG_PREFIX } from \"../../../../core/capabilities/usecase-join.js\";\nimport { stepFindings, type StepGrading } from \"../../fleet/usecases/steps.js\";\nimport { serviceTagFindings, type ServiceTagScope } from \"./tags.js\";\n\n/** What the service's use-case grades run over. */\nexport interface ServiceUseCaseScope extends ServiceTagScope {\n /** The docs root, so a project error's absolute path can be spelled repo-relative. */\n docsDir: DocsDir;\n scan: ServiceFlowScan;\n /**\n * The `services/<id>/` directories that exist — the SAME enumerated set the\n * fleet target hands `stepFindings`, never an empty one: `StepGrading`\n * documents why the intra-service exemption is a guard and not a scope.\n */\n services: ReadonlySet<string>;\n}\n\n/** `<svc>: services/<tree>/<file> — dynamic view '<id>'` — the service target's spelling of `viewPlace`. */\nfunction servicePlace(scope: ServiceUseCaseScope, view: ParsedView): string {\n // An absent `sourcePath` names the directory rather than guessing a file,\n // for `viewFile`'s reason; nothing at the 1.59.2 pin reaches that arm.\n return `${scope.service}: ${scope.treePath}/${view.sourcePath ?? \"\"} — dynamic view '${view.id}'`;\n}\n\n/** The repo-relative spelling of a project error's document, or the whole project when it carries none. */\nfunction fileOf(scope: ServiceUseCaseScope, error: LikeC4Error): string {\n return error.sourceFsPath === undefined\n ? `${scope.treePath}/`\n : relative(scope.docsDir, error.sourceFsPath).split(/[\\\\/]/).join(\"/\");\n}\n\n/**\n * The one finding a broken project earns. `details` is one `errorText` line\n * per error, each prefixed by its file, because the project may hold several\n * siblings and the fault may sit in any of them — including `model.likec4`\n * itself when a sibling re-declares its `specification` block, an error LikeC4\n * blames on both files. `locations` names the first broken file, in the sorted\n * order the reader produced.\n */\nfunction flowInvalidFinding(scope: ServiceUseCaseScope, errors: readonly LikeC4Error[]): Finding {\n const files = [...new Set(errors.map((error) => fileOf(scope, error)))];\n const [first = `${scope.treePath}/`, ...others] = files;\n const named = others.length === 0 ? first : `${first} and ${others.length} more file(s)`;\n return {\n severity: \"error\",\n code: \"usecase.flow-invalid\",\n subject: scope.service,\n message:\n `${scope.service}: ${named} has ${errors.length} error(s) — every .likec4 beside model.likec4 is one LikeC4 ` +\n \"project, read the way the renderer reads it, and the renderer refuses the whole project over one broken \" +\n \"file; no flow in it was graded. model.likec4 itself parses and is graded alone.\",\n // The hint first, for `c4.invalid`'s reason (`../model/grade.ts`):\n // `capDetails` keeps ten lines, one broken document can carry more than ten\n // errors, and a diagnosis appended after them is dropped exactly when the\n // reader needs it.\n details: [...tagHint(errors), ...errors.map((error) => `${fileOf(scope, error)}: ${errorText(error)}`)],\n locations: [{ path: first, role: \"primary\" }],\n };\n}\n\n/** `Could not resolve reference to Tag named '#req-PAY-AUTH'.` — measured at the 1.59.2 pin. */\nconst UNDECLARED_TAG = /reference to Tag named '#?([\\w.-]+)'/;\n\n/**\n * The one line an undeclared RESERVED tag earns.\n *\n * The opt-in is a tag, LikeC4 refuses a tag no `specification` declares, and the\n * refusal is a bare `Could not resolve reference to Tag named …` — so an author\n * following the protocol's \"tag the view `#req-<id>`\" got a parse error naming\n * the thing they had just been told to write, with nothing saying a tag must be\n * declared first (verification 2026-09-04, D10). One line, from the first such\n * error only: the second one has the same repair.\n */\nfunction tagHint(errors: readonly LikeC4Error[]): string[] {\n for (const error of errors) {\n const match = UNDECLARED_TAG.exec(error.message);\n const name = match?.[1];\n if (name === undefined) continue;\n // `cap-` and `req-` do NOT share a repair here, and covering both with one\n // hint sent an author two steps where one would do: declaring `tag cap-…`\n // makes the file parse and the very next run says `usecase.capability-\n // unresolved` — \"no capability can be claimed inside this service's own\n // project. Drop the tag.\" A capability is claimed at fleet altitude, so the\n // tag can never resolve to anything beside a model, whatever is declared\n // (verification 2026-09-04).\n if (name.startsWith(CAP_TAG_PREFIX)) {\n return [\n `\\`#${name}\\` claims a capability, and a capability is claimed at FLEET altitude only — a flow beside ` +\n \"model.likec4 is read in this service's own project, where no capability can be resolved. Declaring \" +\n `\\`tag ${name}\\` makes the file parse and earns \\`usecase.capability-unresolved\\` on the next run: ` +\n `drop the tag, and tag the flow \\`#${REQ_TAG_PREFIX}<Requirement-ID>\\` instead, or draw it in ` +\n \"`architecture/usecases/` where a capability tag belongs\",\n ];\n }\n if (!name.startsWith(REQ_TAG_PREFIX)) continue;\n return [\n `declare \\`tag ${name}\\` in the \\`specification { }\\` block this project reads — model.likec4's own ` +\n \"for a model that stands alone, the fleet map's for one that extends it (or a tags-only \" +\n \"`specification` in a document beside the model)\",\n ];\n }\n return [];\n}\n\n/**\n * The FILE each finding is about, as `locations[0]`.\n *\n * The step and tag grades carry no `locations` of their own, so `findingJson`\n * fell back to the target's scope — `services/<tree>` and nothing else — and an\n * agent reading `--json`, which is the reason the envelope exists, had to parse\n * the prose to learn which `.likec4` to open. `usecase.flow-invalid` on this\n * same arm already names its file; these say the file the same way, with the\n * service directory kept beside it as `scope` (`../spine.ts`'s shape).\n */\nfunction atView(scope: ServiceUseCaseScope, view: ParsedView, findings: Finding[]): Finding[] {\n const file = view.sourcePath === undefined ? `${scope.treePath}/` : `${scope.treePath}/${view.sourcePath}`;\n return findings.map((finding) =>\n finding.locations === undefined\n ? {\n ...finding,\n locations: [\n { path: file, role: \"primary\" as const },\n { path: scope.treePath, role: \"scope\" as const },\n ],\n }\n : finding,\n );\n}\n\n/**\n * Every use-case finding a service's own project earns.\n *\n * Views are sorted by (file, view id) for the fleet grader's reason: nothing in\n * loam has measured that the parse preserves declaration order, and a report\n * whose row order depends on it would reorder under a dependency bump.\n */\nexport function serviceUseCaseFindings(scope: ServiceUseCaseScope): Finding[] {\n const { scan } = scope;\n if (scan.kind === \"none\") return [];\n if (scan.kind === \"unreadable\") return [flowInvalidFinding(scope, scan.errors)];\n\n const place = (view: ParsedView): string => servicePlace(scope, view);\n const grading: StepGrading = {\n model: scan.model,\n services: scope.services,\n // Built over the PROJECT's elements with the fleet set, exactly as the\n // attribution's own fallback tier resolves — so \"which service is this\n // container\" cannot be answered one way by `attributeStep` and another by\n // the provider guard.\n resolve: serviceResolver(scan.model.elements, scan.model.known),\n place,\n };\n const findings: Finding[] = [];\n const views = [...scan.views].sort(\n (a, b) => compareIds(a.sourcePath ?? \"\", b.sourcePath ?? \"\") || compareIds(a.id, b.id),\n );\n for (const view of views) {\n const forView: Finding[] = [...serviceTagFindings(view, scope, place(view))];\n for (const step of view.steps) forView.push(...stepFindings({ view, step }, grading));\n findings.push(...atView(scope, view, forView));\n }\n return findings;\n}\n\n/**\n * The `Requirement-ID`s a service-local `#req-` tag may name: every identified\n * requirement of spec.md (living — a REMOVED one is on its way out and keeps no\n * promise) and of arch.spec.md, as ONE set. `undefined` when NEITHER document\n * exists, which the tag grade reads as \"no requirement to satisfy\" rather than\n * \"none flattening to this slug\" — the same two answers the fleet's\n * `requirementsOf` gives for a capability with no document at all.\n *\n * An id both documents declare resolves once (a set dedupes it); two ids that\n * merely flatten alike — `PAY.AUTH` in one file, `PAY-AUTH` in the other — are\n * the `many` arm, because a Requirement-ID is unique inside one document and a\n * flow beside the model has two.\n *\n * It lives beside the grade it feeds rather than in `../service.ts`, which is\n * the ORDER and nothing else: the set is this axis's input, its two answers are\n * this axis's vocabulary, and the order module had grown past its line limit\n * carrying it.\n */\nexport function requirementIdsOf(\n paths: ServicePaths,\n livingReqs: readonly Requirement[],\n archReqs: readonly Requirement[],\n): ReadonlySet<string> | undefined {\n if (!existsSync(paths.spec) && !existsSync(paths.archSpec)) return undefined;\n const ids = new Set<string>();\n for (const req of [...livingReqs, ...archReqs.filter((r) => r.kind !== \"REMOVED\")]) {\n if (req.id !== undefined) ids.add(req.id);\n }\n return ids;\n}\n"]}
@@ -3,12 +3,12 @@ import { readFile } from "node:fs/promises";
3
3
  import { agentsStaleFinding, binaryBehindFinding } from "../../core/agent/agents-stamp.js";
4
4
  import { inOrder } from "../../core/kernel/concurrency.js";
5
5
  import { resolveServiceTarget } from "../../core/repo/service-target.js";
6
- import { loadConfig } from "../../core/envelope/config.js";
7
- import { emitJson, fail, NO_SERVICE_MESSAGE, reportNoConfig } from "../../core/envelope/json.js";
6
+ import { loamOwnedFiles } from "../../core/envelope/config.js";
7
+ import { configOrRefuse, emitJson, fail, NO_SERVICE_MESSAGE } from "../../core/envelope/json.js";
8
8
  import { DocsRepoUnavailableError } from "../../core/repo/state.js";
9
9
  import { agentsPath as agentsFile, landscapePath as landscapeFile } from "../../core/repo/paths.js";
10
10
  import { listFeatures, listServices, missingFeatureMessage, resolveFeature } from "../../core/repo/repo.js";
11
- import { countSeverity, reportValid, targetJson, targetValid, } from "../../core/vocabulary/report.js";
11
+ import { countSeverity, reportValid, targetJson } from "../../core/vocabulary/report.js";
12
12
  import { LOAM_VERSION } from "../../core/envelope/version.js";
13
13
  import { FleetContext } from "../../core/fleet-context.js";
14
14
  import { fixesFor } from "../policy/format.js";
@@ -21,6 +21,7 @@ import { buildScorecard } from "./fleet/scorecard/scorecard.js";
21
21
  import { printScorecard } from "./fleet/scorecard/print.js";
22
22
  import { validateFeature } from "./feature.js";
23
23
  import { validateService } from "./service/service.js";
24
+ import { scopedLandscapeFindings } from "./scope.js";
24
25
  import { ambiguousTarget, capDetails, guarded, renderText, summary, unverifiableSubjects } from "./report.js";
25
26
  export function registerValidate(program) {
26
27
  program
@@ -62,12 +63,9 @@ export function registerValidate(program) {
62
63
  : "--base narrows --all to what changed since a ref — pass --all with it.");
63
64
  return;
64
65
  }
65
- const loaded = await loadConfig();
66
- if (loaded.kind !== "loaded") {
67
- reportNoConfig(json, loaded);
66
+ const config = await configOrRefuse(json);
67
+ if (config === null)
68
68
  return;
69
- }
70
- const config = loaded.config;
71
69
  const { docsDir } = config;
72
70
  // Before anything is enumerated: a docsDir that is not a docs repo must
73
71
  // refuse, not report an empty fleet. See docsRepoReady. `--feature` is the
@@ -81,8 +79,30 @@ export function registerValidate(program) {
81
79
  // something when loam is standing in that repository — which is exactly
82
80
  // what loam.json's `service` records.
83
81
  const repoOf = (service) => config.service === service ? process.cwd() : undefined;
84
- /** One service target, from a name the enumeration or the grammar approved. */
85
- const serviceTarget = (id) => guarded({ kind: "service", id }, () => validateService({ docsDir, service: id, repoDir: repoOf(id), gherkinDir: config.gherkinDir, contracts: config.contracts, fleet }));
82
+ // What loam itself wrote into that repository, which no `sources` list owes a reading of.
83
+ const loamOwned = loamOwnedFiles(config);
84
+ /**
85
+ * One service target, from a name the enumeration or the grammar approved.
86
+ * `land` is --all's landscape, read once for the whole run and handed to
87
+ * the service. Null is PASSED — it means "there is no landscape file" —
88
+ * so the test is `=== undefined`, never truthiness: a repo with no
89
+ * landscape would otherwise re-load per service and double-report.
90
+ *
91
+ * `landscapeReported` says whether this run ALSO emits a landscape target
92
+ * to carry the map's parse errors, and it is the caller's to state: a
93
+ * preloaded map does not imply one. It used to be read off `land`, and
94
+ * under `--base` the landscape is a target only when the branch changed
95
+ * `architecture/` — so a broken committed map and a branch touching only
96
+ * a service told that service "reported once, on the landscape target"
97
+ * with no details, and the parser output was reported nowhere.
98
+ */
99
+ const serviceTarget = (id, land, landscapeReported = false) => guarded({ kind: "service", id }, () => validateService({
100
+ docsDir, service: id, repoDir: repoOf(id), loamOwned, gherkinDir: config.gherkinDir, contracts: config.contracts, fleet,
101
+ ...(land === undefined ? {} : { preloaded: land }),
102
+ landscapeReported,
103
+ }));
104
+ /** One feature target; `land` as above, and left undefined it means "load it if you need it". */
105
+ const featureTarget = (feature, land) => guarded({ kind: "feature", id: feature.id }, () => validateFeature(docsDir, feature, land, fleet));
86
106
  const targets = [];
87
107
  /** What the positional argument turned out to name, for the JSON payload. */
88
108
  let resolvedKind;
@@ -121,8 +141,11 @@ export function registerValidate(program) {
121
141
  // The fleet-level cross-check first: it frames everything below it, and a
122
142
  // service nobody drew is worth knowing before its own findings scroll past.
123
143
  // Under `--base` it is a target only when `architecture/` changed — the
124
- // landscape owns no service directory, so that is its whole footprint.
125
- if (scope === null || scope.landscape) {
144
+ // landscape owns no service directory, so that is its whole footprint —
145
+ // and even then not whole: `scope.ts` drops what grades a file the
146
+ // branch left alone and adds the call edges it wrote.
147
+ const landscapeIsTarget = scope === null || scope.landscape;
148
+ if (landscapeIsTarget) {
126
149
  const landscape = await validateLandscape(docsDir, land, fleet);
127
150
  targets.push(landscape);
128
151
  // The agent contract check, --all only: AGENTS.md is written once and
@@ -142,18 +165,14 @@ export function registerValidate(program) {
142
165
  const behind = binaryBehindFinding(agentsText, LOAM_VERSION);
143
166
  if (behind !== null)
144
167
  landscape.findings.push(behind);
168
+ if (scope !== null) {
169
+ landscape.findings = await scopedLandscapeFindings(landscape.findings, {
170
+ docsDir, scope, land, services: enumerated, fleet,
171
+ });
172
+ }
145
173
  }
146
- targets.push(...(await inOrder(services, (svc) => guarded({ kind: "service", id: svc.id }, () => validateService({
147
- docsDir,
148
- service: svc.id,
149
- repoDir: repoOf(svc.id),
150
- preloaded: land,
151
- gherkinDir: config.gherkinDir,
152
- contracts: config.contracts,
153
- fleet,
154
- landscapeReported: true,
155
- })))));
156
- targets.push(...(await inOrder(features, (feat) => guarded({ kind: "feature", id: feat.id }, () => validateFeature(docsDir, feat, land, fleet)))));
174
+ targets.push(...(await inOrder(services, (svc) => serviceTarget(svc.id, land, landscapeIsTarget))));
175
+ targets.push(...(await inOrder(features, (feat) => featureTarget(feat, land))));
157
176
  }
158
177
  else if (opts.feature) {
159
178
  const feature = await resolveFeature(docsDir, opts.feature, "exclude", fleet);
@@ -161,7 +180,7 @@ export function registerValidate(program) {
161
180
  fail(json, "unknown-target", await missingFeatureMessage(docsDir, opts.feature, fleet));
162
181
  return;
163
182
  }
164
- targets.push(await guarded({ kind: "feature", id: feature.id }, () => validateFeature(docsDir, feature, undefined, fleet)));
183
+ targets.push(await featureTarget(feature));
165
184
  }
166
185
  else if (target !== undefined) {
167
186
  // The positional reads the way `show` reads one: try the feature first
@@ -173,7 +192,7 @@ export function registerValidate(program) {
173
192
  const isService = (await listServices(docsDir, fleet)).some((s) => s.id === target);
174
193
  if (feature) {
175
194
  resolvedKind = "feature";
176
- const report = await guarded({ kind: "feature", id: feature.id }, () => validateFeature(docsDir, feature, undefined, fleet));
195
+ const report = await featureTarget(feature);
177
196
  if (isService)
178
197
  report.findings.unshift(ambiguousTarget(target, "feature"));
179
198
  targets.push(report);
@@ -320,5 +339,4 @@ export function registerValidate(program) {
320
339
  process.exitCode = 1;
321
340
  });
322
341
  }
323
- export { targetValid };
324
342
  //# sourceMappingURL=validate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/commands/validate/validate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam validate` — argument grammar, target selection, and the run's one\n * filesystem snapshot.\n *\n * The checks themselves live beside it, one module per target kind\n * (`landscape.ts`, `service/`, `feature/`) over the document checks they share\n * (`requirements.ts`). Every one of them produces findings and none of them\n * print: printing is `report.ts`'s, and it happens once, after the last target\n * is graded.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { agentsStaleFinding, binaryBehindFinding } from \"../../core/agent/agents-stamp.js\";\nimport { inOrder } from \"../../core/kernel/concurrency.js\";\nimport type { RawServiceId } from \"../../core/kernel/ids/service.js\";\nimport { resolveServiceTarget } from \"../../core/repo/service-target.js\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { emitJson, fail, NO_SERVICE_MESSAGE, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { agentsPath as agentsFile, landscapePath as landscapeFile } from \"../../core/repo/paths.js\";\nimport { listFeatures, listServices, missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport {\n countSeverity,\n reportValid,\n targetJson,\n targetValid,\n type TargetReport,\n} from \"../../core/vocabulary/report.js\";\nimport { LOAM_VERSION } from \"../../core/envelope/version.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { fixesFor } from \"../policy/format.js\";\nimport {\n docsRepoReady,\n reportDocsRepoError,\n reportRepositoryUnavailable,\n} from \"../policy/gate.js\";\nimport { interruptedCommitFinding } from \"../../core/staging/recovery/finding.js\";\nimport { scopeJson, scopeLine, scopeSince, type ValidateScope } from \"../../core/diff/scope/changed-paths.js\";\nimport { validateLandscape } from \"./fleet/landscape.js\";\nimport { prefetchFleetDocuments, readLandscape } from \"./fleet/load.js\";\nimport { buildScorecard } from \"./fleet/scorecard/scorecard.js\";\nimport { printScorecard } from \"./fleet/scorecard/print.js\";\nimport { validateFeature } from \"./feature.js\";\nimport { validateService } from \"./service/service.js\";\nimport { ambiguousTarget, capDetails, guarded, renderText, summary, unverifiableSubjects } from \"./report.js\";\n\ninterface ValidateOptions {\n service?: string;\n feature?: string;\n all?: boolean;\n base?: string;\n strict?: boolean;\n errorsOnly?: boolean;\n json?: boolean;\n}\n\nexport function registerValidate(program: Command): void {\n program\n .command(\"validate\")\n .argument(\"[target]\", \"service or feature id (a feature wins when both match; --service/--feature force the reading)\")\n .description(\"Validate a service (C4 + requirement coverage) or a feature (delta + coverage)\")\n .option(\"--service <id>\", \"service to validate (defaults to the configured service)\")\n .option(\"--feature <id>\", \"validate a feature delta instead of a service\")\n .option(\"--all\", \"validate every service and every active feature\")\n .option(\"--base <ref>\", \"with --all: grade only the targets changed since a base git ref of the docs repo — the adoption ratchet\")\n .option(\"--strict\", \"exit 1 on any warning too — a per-invocation CI lever; the report and --json payload do not change\")\n .option(\"--errors-only\", \"print only errors and warnings — a rendering lever; the --json payload does not change\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (target: string | undefined, opts: ValidateOptions) => {\n const json = opts.json === true;\n\n if (target !== undefined && (opts.all || opts.service || opts.feature)) {\n fail(json, \"invalid-option\", `'${target}' already names the target; drop --all/--service/--feature.`);\n return;\n }\n if (opts.all && (opts.service || opts.feature)) {\n fail(json, \"invalid-option\", \"--all validates everything; drop --service/--feature.\");\n return;\n }\n // Two targets is no target: silently validating only the feature taught\n // callers that --service had been honoured when it had been dropped.\n if (opts.service && opts.feature) {\n fail(json, \"invalid-option\", \"--service and --feature name different targets; pass one or the other.\");\n return;\n }\n // `--base` NARROWS a whole-fleet run; it does not name one. Beside a\n // positional, `--service` or `--feature` it is a scope over a scope — a\n // contradiction rather than a narrowing, whose \"this service if it\n // changed\" reading nobody could tell apart from a green over a service\n // never looked at. `--base` and not `--since`: `--base <ref>` is already\n // loam's word for a base git ref of the docs repo on diff and vouch.\n if (opts.base !== undefined && opts.all !== true) {\n fail(\n json,\n \"invalid-option\",\n target !== undefined || opts.service || opts.feature\n ? \"--base narrows --all to what changed since a ref; a named target is already a scope. Pass one or the other.\"\n : \"--base narrows --all to what changed since a ref — pass --all with it.\",\n );\n return;\n }\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const { docsDir } = config;\n // Before anything is enumerated: a docsDir that is not a docs repo must\n // refuse, not report an empty fleet. See docsRepoReady. `--feature` is the\n // one mode that never enumerates services, so it asks for less.\n if (!docsRepoReady(json, docsDir, opts.feature ? \"docs\" : \"services\")) return;\n // One invocation, one filesystem snapshot. Nothing is global: a later\n // command gets a fresh index and therefore cannot observe stale files.\n const fleet = new FleetContext();\n\n // `sources` are paths into a service's own repository, so they only mean\n // something when loam is standing in that repository — which is exactly\n // what loam.json's `service` records.\n const repoOf = (service: string): string | undefined =>\n config.service === service ? process.cwd() : undefined;\n\n /** One service target, from a name the enumeration or the grammar approved. */\n const serviceTarget = (id: RawServiceId): Promise<TargetReport> =>\n guarded({ kind: \"service\", id }, () =>\n validateService({ docsDir, service: id, repoDir: repoOf(id), gherkinDir: config.gherkinDir, contracts: config.contracts, fleet }),\n );\n\n const targets: TargetReport[] = [];\n /** What the positional argument turned out to name, for the JSON payload. */\n let resolvedKind: \"service\" | \"feature\" | undefined;\n /** The `--base` narrowing, once it is known. Null on every unscoped run — including every run without --base. */\n let scope: ValidateScope | null = null;\n\n try {\n if (opts.all) {\n // ONE workspace per document kind for the whole run — the block moved\n // to `fleet/load.ts` (`prefetchFleetDocuments`), which is where the\n // rule about which shape parses in which workspace belongs.\n const lp = landscapeFile(docsDir);\n const enumerated = await listServices(docsDir, fleet);\n const active = await listFeatures(docsDir, {}, fleet);\n // `--base` narrows the enumerations and nothing else: a kept target is\n // graded by exactly the checks `--all` would have run on it, at the\n // same severities, through the same `--strict`. It matches through the\n // ENUMERATIONS' own directories — changed-paths.ts says why a split\n // on \"/\" silently drops every filed service.\n if (opts.base !== undefined) {\n const narrowed = await scopeSince({ docsDir, ref: opts.base, services: enumerated, features: active });\n if (narrowed.kind === \"refused\") {\n fail(json, narrowed.code, narrowed.message);\n return;\n }\n scope = narrowed;\n }\n const services = scope === null ? enumerated : scope.services;\n const features = scope === null ? active : scope.features;\n await prefetchFleetDocuments({ docsDir, fleet, services, features });\n // Read whether or not the landscape is a TARGET: every service check\n // below joins against it, so a scoped run needs the map in hand even\n // when it does not grade the map itself. Through the fleet index, so\n // the single-service path — which now reads the same project — answers\n // out of the same memo rather than parsing it a second time.\n const land = existsSync(lp) ? await readLandscape(() => fleet.architecture(docsDir)) : null;\n // The fleet-level cross-check first: it frames everything below it, and a\n // service nobody drew is worth knowing before its own findings scroll past.\n // Under `--base` it is a target only when `architecture/` changed — the\n // landscape owns no service directory, so that is its whole footprint.\n if (scope === null || scope.landscape) {\n const landscape = await validateLandscape(docsDir, land, fleet);\n targets.push(landscape);\n // The agent contract check, --all only: AGENTS.md is written once and\n // never refreshed (the ownership contract), so the one thing the\n // docs-repo-wide mode owes it is detection — a stamp older than the\n // binary means agents are branching on tables the binary no longer\n // honours. It grades the repo, not any service, so it rides on the\n // landscape target.\n const agentsPath = agentsFile(docsDir);\n const agentsText = existsSync(agentsPath) ? await readFile(agentsPath, \"utf8\") : null;\n const agents = agentsStaleFinding(agentsText, LOAM_VERSION);\n if (agents !== null) landscape.findings.push(agents);\n // The other direction, and the one that changes what a PASS means:\n // this binary predates the corpus, so a directive it cannot parse is\n // read as prose and produces no join to fail.\n const behind = binaryBehindFinding(agentsText, LOAM_VERSION);\n if (behind !== null) landscape.findings.push(behind);\n }\n targets.push(\n ...(await inOrder(services, (svc) =>\n guarded({ kind: \"service\", id: svc.id }, () =>\n validateService({\n docsDir,\n service: svc.id,\n repoDir: repoOf(svc.id),\n preloaded: land,\n gherkinDir: config.gherkinDir,\n contracts: config.contracts,\n fleet,\n landscapeReported: true,\n }),\n ),\n )),\n );\n targets.push(\n ...(await inOrder(features, (feat) =>\n guarded({ kind: \"feature\", id: feat.id }, () =>\n validateFeature(docsDir, feat, land, fleet),\n ),\n )),\n );\n } else if (opts.feature) {\n const feature = await resolveFeature(docsDir, opts.feature, \"exclude\", fleet);\n if (!feature) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, opts.feature, fleet));\n return;\n }\n targets.push(\n await guarded({ kind: \"feature\", id: feature.id }, () =>\n validateFeature(docsDir, feature, undefined, fleet),\n ),\n );\n } else if (target !== undefined) {\n // The positional reads the way `show` reads one: try the feature first\n // (ids like FEAT-101 are distinctive, service names are arbitrary), then\n // the service. --service/--feature stay as the explicit spellings for a\n // name that could be both — and when both readings exist, the run says\n // so out loud instead of silently taking one (`target.ambiguous`).\n const feature = await resolveFeature(docsDir, target, \"exclude\", fleet);\n const isService = (await listServices(docsDir, fleet)).some((s) => s.id === target);\n if (feature) {\n resolvedKind = \"feature\";\n const report = await guarded({ kind: \"feature\", id: feature.id }, () =>\n validateFeature(docsDir, feature, undefined, fleet),\n );\n if (isService) report.findings.unshift(ambiguousTarget(target, \"feature\"));\n targets.push(report);\n } else {\n // Neither reading exists. An archived feature is its own diagnosis\n // (\"already archived\", not \"no such thing\"); anything else reads as a\n // service, so the did-you-mean hints in service.unknown fire.\n if (!isService && (await resolveFeature(docsDir, target, \"only\", fleet)) !== null) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, target, fleet));\n return;\n }\n resolvedKind = \"service\";\n const resolved = await resolveServiceTarget(docsDir, target, \"target\", fleet);\n if (!resolved.ok) return fail(json, \"invalid-option\", resolved.problem);\n targets.push(await serviceTarget(resolved.id));\n }\n } else {\n const service = opts.service ?? config.service;\n if (!service) {\n fail(json, \"invalid-option\", NO_SERVICE_MESSAGE);\n return;\n }\n const resolved = await resolveServiceTarget(docsDir, service, \"--service\", fleet);\n if (!resolved.ok) return fail(json, \"invalid-option\", resolved.problem);\n targets.push(await serviceTarget(resolved.id));\n }\n } catch (err) {\n // The docs repo went away between the gate above and the enumeration.\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // An IO failure that `guarded` could not localise to one target — it\n // happened in the ENUMERATION, which reads every living spec's\n // frontmatter to build the service list. There is no partial report to\n // give: without the list, no service was checked, including the ones\n // that are fine. Say which file, and refuse; the one thing that must\n // not happen is a stack trace on stdout's sibling stream and an\n // `internal` envelope that names nothing.\n reportRepositoryUnavailable(\n json,\n err,\n \"the service list itself is unknown and nothing was validated\",\n docsDir,\n );\n return;\n }\n\n // By subject, not by finding — the rule lives with the rollup in report.ts.\n const unverifiable = unverifiableSubjects(targets);\n // A journal in the repo outranks everything graded above: the docs may\n // be half-merged, and in CI a green over them merges. Led, not appended\n // — it is the reason nothing below can be trusted — and attached to the\n // first target because a finding lives in one.\n const interrupted = await interruptedCommitFinding(docsDir);\n if (interrupted !== null && targets.length > 0) targets[0]!.findings.unshift(interrupted);\n\n // The fleet scorecard, --all only: recomputed per invocation off the same\n // memoized reads this run already made — loam stores no history — and it\n // fails closed per axis, never whole (fleet/scorecard/scorecard.ts).\n //\n // Suppressed under `--base`: its denominators are the graded targets, so\n // over a narrowed set it reads \"2 of 2 services documented\" for a fleet\n // where ten more are not — and its axes license report.ts's grouping,\n // which would drop real warnings because an unlooked-at axis reads zero.\n const scorecard =\n opts.all && scope === null\n ? await buildScorecard({ docsDir, targets, fleet, boundService: config.service })\n : null;\n\n const valid = reportValid(targets);\n const capped = targets.map(capDetails);\n if (json) {\n emitJson({\n command: \"validate\",\n valid,\n summary: summary(targets),\n ...(resolvedKind === undefined ? {} : { resolvedKind }),\n // What this run looked at. Only under `--base`, so an unscoped payload\n // is today's document and its ABSENCE says `summary` counts the whole\n // fleet — scopeJson has why a scoped one may not stay silent.\n ...(scope === null ? {} : { scope: scopeJson(scope) }),\n // Emitted in every mode now that it is derived per service: a\n // single-service run knows exactly as much about its own blind spot\n // as `--all` knows about the fleet's.\n sourcesUnverifiableFromHere: unverifiable,\n ...(scorecard === null ? {} : { scorecard }),\n // What to do about every code this run raised — the one thing the\n // payload could not previously answer. `validate` is step 1 of every\n // generated protocol and the command an agent reads first, and until\n // now it replied in prose: a finding names a code and says what is\n // wrong, and nothing in it said that `loam explain <code>` knows what\n // to do next.\n //\n // A top-level MAP, not a field on each finding, for the reason\n // report.ts's DETAIL_LIMIT gives about evidence: a hundred-service\n // fleet raising one code a hundred times would ship the same\n // sentence a hundred times, in the interface an agent pipes into a\n // context window. The consumer joins `findings[].code` to\n // `fixes[code]` — one lookup, no round trip, no repetition.\n //\n // Keyed ONLY by codes a non-`ok` finding raised. A confirmation has\n // nothing to fix, and on a clean fleet the `ok` codes are the\n // overwhelming majority of findings; attaching fixes to them would\n // pay the whole vocabulary's byte cost to tell a reader that nothing\n // is wrong. `targets` is read here rather than `capped` because\n // capping truncates evidence, not findings — the two raise the same\n // codes, and reading the uncapped set keeps this rollup computed off\n // the same targets as `summary()`.\n //\n // Not behind a flag, deliberately. It is additive, so no consumer\n // that ignores it is affected; it is small, because it carries only\n // the handful of codes this run actually raised rather than the\n // 227-row vocabulary; and a flag would mean an agent has to already\n // know the fixes exist in order to ask for them — which is the exact\n // gap being closed. It is emitted on a green run too, as `{}`, so a\n // consumer never has to branch on the key's presence before looking\n // a code up in it.\n fixes: fixesFor(\n targets.flatMap((t) => t.findings.filter((f) => f.severity !== \"ok\").map((f) => f.code)),\n ),\n targets: capped.map(targetJson),\n });\n } else {\n // The scope FIRST: a reader has to know what the report below is a\n // report ABOUT, and in the zero-target case there is nothing below at\n // all — printed after the findings, the one run that most needs words\n // would read as a green over the whole system.\n if (scope !== null) console.log(scopeLine(scope));\n renderText(capped, {\n all: opts.all === true,\n errorsOnly: opts.errorsOnly === true,\n unverifiable,\n scorecard,\n });\n if (scorecard !== null) printScorecard(scorecard);\n }\n // --strict is a per-invocation CI lever: it fails the run on any error or\n // warning (ok-severity confirmations never trip it — virtually every\n // clean target emits some), and changes nothing else — `valid` keeps meaning \"no\n // errors\" in text and JSON alike, so two pipelines reading the same repo\n // may grade the same report differently and both are telling the truth.\n const strictFailed =\n opts.strict === true &&\n countSeverity(targets, \"error\") + countSeverity(targets, \"warn\") > 0;\n if (!valid || strictFailed) process.exitCode = 1;\n });\n}\n\nexport { targetValid };\n"]}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/commands/validate/validate.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC3F,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjG,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,UAAU,IAAI,UAAU,EAAE,aAAa,IAAI,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC5G,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAqB,MAAM,iCAAiC,CAAC;AAC5G,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAsB,MAAM,wCAAwC,CAAC;AAC9G,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAY9G,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CAAC,UAAU,EAAE,+FAA+F,CAAC;SACrH,WAAW,CAAC,gFAAgF,CAAC;SAC7F,MAAM,CAAC,gBAAgB,EAAE,0DAA0D,CAAC;SACpF,MAAM,CAAC,gBAAgB,EAAE,+CAA+C,CAAC;SACzE,MAAM,CAAC,OAAO,EAAE,iDAAiD,CAAC;SAClE,MAAM,CAAC,cAAc,EAAE,yGAAyG,CAAC;SACjI,MAAM,CAAC,UAAU,EAAE,oGAAoG,CAAC;SACxH,MAAM,CAAC,eAAe,EAAE,wFAAwF,CAAC;SACjH,MAAM,CAAC,QAAQ,EAAE,qDAAqD,CAAC;SACvE,MAAM,CAAC,KAAK,EAAE,MAA0B,EAAE,IAAqB,EAAE,EAAE;QAClE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAEhC,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,MAAM,6DAA6D,CAAC,CAAC;YACtG,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,uDAAuD,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QACD,wEAAwE;QACxE,qEAAqE;QACrE,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,wEAAwE,CAAC,CAAC;YACvG,OAAO;QACT,CAAC;QACD,qEAAqE;QACrE,wEAAwE;QACxE,mEAAmE;QACnE,uEAAuE;QACvE,yEAAyE;QACzE,qEAAqE;QACrE,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;YACjD,IAAI,CACF,IAAI,EACJ,gBAAgB,EAChB,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO;gBAClD,CAAC,CAAC,6GAA6G;gBAC/G,CAAC,CAAC,wEAAwE,CAC7E,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO;QAC5B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAC3B,wEAAwE;QACxE,2EAA2E;QAC3E,gEAAgE;QAChE,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;YAAE,OAAO;QAC9E,sEAAsE;QACtE,uEAAuE;QACvE,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;QAEjC,yEAAyE;QACzE,wEAAwE;QACxE,sCAAsC;QACtC,MAAM,MAAM,GAAG,CAAC,OAAe,EAAsB,EAAE,CACrD,MAAM,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACzD,0FAA0F;QAC1F,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAEzC;;;;;;;;;;;;;;WAcG;QACH,MAAM,aAAa,GAAG,CAAC,EAAgB,EAAE,IAAuB,EAAE,iBAAiB,GAAG,KAAK,EAAyB,EAAE,CACpH,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CACpC,eAAe,CAAC;YACd,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK;YACvH,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YAClD,iBAAiB;SAClB,CAAC,CACH,CAAC;QACJ,iGAAiG;QACjG,MAAM,aAAa,GAAG,CAAC,OAAqB,EAAE,IAAuB,EAAyB,EAAE,CAC9F,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAErG,MAAM,OAAO,GAAmB,EAAE,CAAC;QACnC,6EAA6E;QAC7E,IAAI,YAA+C,CAAC;QACpD,iHAAiH;QACjH,IAAI,KAAK,GAAyB,IAAI,CAAC;QAEvC,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;gBACb,sEAAsE;gBACtE,oEAAoE;gBACpE,4DAA4D;gBAC5D,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;gBAClC,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBACtD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;gBACtD,uEAAuE;gBACvE,oEAAoE;gBACpE,uEAAuE;gBACvE,oEAAoE;gBACpE,6CAA6C;gBAC7C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC5B,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;oBACvG,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;wBAChC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;wBAC5C,OAAO;oBACT,CAAC;oBACD,KAAK,GAAG,QAAQ,CAAC;gBACnB,CAAC;gBACD,MAAM,QAAQ,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC9D,MAAM,QAAQ,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC1D,MAAM,sBAAsB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,uEAAuE;gBACvE,6DAA6D;gBAC7D,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC5F,0EAA0E;gBAC1E,4EAA4E;gBAC5E,wEAAwE;gBACxE,wEAAwE;gBACxE,mEAAmE;gBACnE,sDAAsD;gBACtD,MAAM,iBAAiB,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC;gBAC5D,IAAI,iBAAiB,EAAE,CAAC;oBACtB,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;oBAChE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACxB,sEAAsE;oBACtE,iEAAiE;oBACjE,oEAAoE;oBACpE,mEAAmE;oBACnE,mEAAmE;oBACnE,oBAAoB;oBACpB,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;oBACvC,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;oBACtF,MAAM,MAAM,GAAG,kBAAkB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;oBAC5D,IAAI,MAAM,KAAK,IAAI;wBAAE,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACrD,mEAAmE;oBACnE,qEAAqE;oBACrE,8CAA8C;oBAC9C,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;oBAC7D,IAAI,MAAM,KAAK,IAAI;wBAAE,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACrD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;wBACnB,SAAS,CAAC,QAAQ,GAAG,MAAM,uBAAuB,CAAC,SAAS,CAAC,QAAQ,EAAE;4BACrE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK;yBAClD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAClF,CAAC;iBAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACxB,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;gBAC9E,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;oBACxF,OAAO;gBACT,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,uEAAuE;gBACvE,yEAAyE;gBACzE,wEAAwE;gBACxE,uEAAuE;gBACvE,mEAAmE;gBACnE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;gBACxE,MAAM,SAAS,GAAG,CAAC,MAAM,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;gBACpF,IAAI,OAAO,EAAE,CAAC;oBACZ,YAAY,GAAG,SAAS,CAAC;oBACzB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;oBAC5C,IAAI,SAAS;wBAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;oBAC3E,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACN,mEAAmE;oBACnE,sEAAsE;oBACtE,8DAA8D;oBAC9D,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;wBAClF,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;wBAClF,OAAO;oBACT,CAAC;oBACD,YAAY,GAAG,SAAS,CAAC;oBACzB,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAC9E,IAAI,CAAC,QAAQ,CAAC,EAAE;wBAAE,OAAO,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;oBACxE,OAAO,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC;gBAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;oBACjD,OAAO;gBACT,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;gBAClF,IAAI,CAAC,QAAQ,CAAC,EAAE;oBAAE,OAAO,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACxE,OAAO,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sEAAsE;YACtE,IAAI,GAAG,YAAY,wBAAwB,EAAE,CAAC;gBAC5C,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,qEAAqE;YACrE,+DAA+D;YAC/D,uEAAuE;YACvE,qEAAqE;YACrE,qEAAqE;YACrE,gEAAgE;YAChE,0CAA0C;YAC1C,2BAA2B,CACzB,IAAI,EACJ,GAAG,EACH,8DAA8D,EAC9D,OAAO,CACR,CAAC;YACF,OAAO;QACT,CAAC;QAED,4EAA4E;QAC5E,MAAM,YAAY,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACnD,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,+CAA+C;QAC/C,MAAM,WAAW,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAC5D,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAE1F,0EAA0E;QAC1E,yEAAyE;QACzE,qEAAqE;QACrE,EAAE;QACF,yEAAyE;QACzE,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,MAAM,SAAS,GACb,IAAI,CAAC,GAAG,IAAI,KAAK,KAAK,IAAI;YACxB,CAAC,CAAC,MAAM,cAAc,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YACjF,CAAC,CAAC,IAAI,CAAC;QAEX,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,IAAI,EAAE,CAAC;YACT,QAAQ,CAAC;gBACP,OAAO,EAAE,UAAU;gBACnB,KAAK;gBACL,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;gBACzB,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;gBACvD,uEAAuE;gBACvE,sEAAsE;gBACtE,8DAA8D;gBAC9D,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtD,8DAA8D;gBAC9D,oEAAoE;gBACpE,sCAAsC;gBACtC,2BAA2B,EAAE,YAAY;gBACzC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;gBAC5C,kEAAkE;gBAClE,qEAAqE;gBACrE,qEAAqE;gBACrE,mEAAmE;gBACnE,sEAAsE;gBACtE,cAAc;gBACd,EAAE;gBACF,+DAA+D;gBAC/D,mEAAmE;gBACnE,6DAA6D;gBAC7D,mEAAmE;gBACnE,0DAA0D;gBAC1D,4DAA4D;gBAC5D,EAAE;gBACF,oEAAoE;gBACpE,8DAA8D;gBAC9D,mEAAmE;gBACnE,qEAAqE;gBACrE,gEAAgE;gBAChE,oEAAoE;gBACpE,qEAAqE;gBACrE,mCAAmC;gBACnC,EAAE;gBACF,kEAAkE;gBAClE,oEAAoE;gBACpE,gEAAgE;gBAChE,oEAAoE;gBACpE,qEAAqE;gBACrE,oEAAoE;gBACpE,oEAAoE;gBACpE,mBAAmB;gBACnB,KAAK,EAAE,QAAQ,CACb,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CACzF;gBACD,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;aAChC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,mEAAmE;YACnE,sEAAsE;YACtE,sEAAsE;YACtE,+CAA+C;YAC/C,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAClD,UAAU,CAAC,MAAM,EAAE;gBACjB,GAAG,EAAE,IAAI,CAAC,GAAG,KAAK,IAAI;gBACtB,UAAU,EAAE,IAAI,CAAC,UAAU,KAAK,IAAI;gBACpC,YAAY;gBACZ,SAAS;aACV,CAAC,CAAC;YACH,IAAI,SAAS,KAAK,IAAI;gBAAE,cAAc,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,0EAA0E;QAC1E,qEAAqE;QACrE,iFAAiF;QACjF,yEAAyE;QACzE,wEAAwE;QACxE,MAAM,YAAY,GAChB,IAAI,CAAC,MAAM,KAAK,IAAI;YACpB,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QACvE,IAAI,CAAC,KAAK,IAAI,YAAY;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["/**\n * `loam validate` — argument grammar, target selection, and the run's one\n * filesystem snapshot.\n *\n * The checks themselves live beside it, one module per target kind\n * (`landscape.ts`, `service/`, `feature/`) over the document checks they share\n * (`requirements.ts`). Every one of them produces findings and none of them\n * print: printing is `report.ts`'s, and it happens once, after the last target\n * is graded.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { agentsStaleFinding, binaryBehindFinding } from \"../../core/agent/agents-stamp.js\";\nimport { inOrder } from \"../../core/kernel/concurrency.js\";\nimport type { RawServiceId } from \"../../core/kernel/ids/service.js\";\nimport { resolveServiceTarget } from \"../../core/repo/service-target.js\";\nimport type { LoadedDoc } from \"../../core/c4/likec4.js\";\nimport type { FeatureEntry } from \"../../core/repo/entries.js\";\nimport { loamOwnedFiles } from \"../../core/envelope/config.js\";\nimport { configOrRefuse, emitJson, fail, NO_SERVICE_MESSAGE } from \"../../core/envelope/json.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { agentsPath as agentsFile, landscapePath as landscapeFile } from \"../../core/repo/paths.js\";\nimport { listFeatures, listServices, missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport { countSeverity, reportValid, targetJson, type TargetReport } from \"../../core/vocabulary/report.js\";\nimport { LOAM_VERSION } from \"../../core/envelope/version.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { fixesFor } from \"../policy/format.js\";\nimport {\n docsRepoReady,\n reportDocsRepoError,\n reportRepositoryUnavailable,\n} from \"../policy/gate.js\";\nimport { interruptedCommitFinding } from \"../../core/staging/recovery/finding.js\";\nimport { scopeJson, scopeLine, scopeSince, type ValidateScope } from \"../../core/diff/scope/changed-paths.js\";\nimport { validateLandscape } from \"./fleet/landscape.js\";\nimport { prefetchFleetDocuments, readLandscape } from \"./fleet/load.js\";\nimport { buildScorecard } from \"./fleet/scorecard/scorecard.js\";\nimport { printScorecard } from \"./fleet/scorecard/print.js\";\nimport { validateFeature } from \"./feature.js\";\nimport { validateService } from \"./service/service.js\";\nimport { scopedLandscapeFindings } from \"./scope.js\";\nimport { ambiguousTarget, capDetails, guarded, renderText, summary, unverifiableSubjects } from \"./report.js\";\n\ninterface ValidateOptions {\n service?: string;\n feature?: string;\n all?: boolean;\n base?: string;\n strict?: boolean;\n errorsOnly?: boolean;\n json?: boolean;\n}\n\nexport function registerValidate(program: Command): void {\n program\n .command(\"validate\")\n .argument(\"[target]\", \"service or feature id (a feature wins when both match; --service/--feature force the reading)\")\n .description(\"Validate a service (C4 + requirement coverage) or a feature (delta + coverage)\")\n .option(\"--service <id>\", \"service to validate (defaults to the configured service)\")\n .option(\"--feature <id>\", \"validate a feature delta instead of a service\")\n .option(\"--all\", \"validate every service and every active feature\")\n .option(\"--base <ref>\", \"with --all: grade only the targets changed since a base git ref of the docs repo — the adoption ratchet\")\n .option(\"--strict\", \"exit 1 on any warning too — a per-invocation CI lever; the report and --json payload do not change\")\n .option(\"--errors-only\", \"print only errors and warnings — a rendering lever; the --json payload does not change\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (target: string | undefined, opts: ValidateOptions) => {\n const json = opts.json === true;\n\n if (target !== undefined && (opts.all || opts.service || opts.feature)) {\n fail(json, \"invalid-option\", `'${target}' already names the target; drop --all/--service/--feature.`);\n return;\n }\n if (opts.all && (opts.service || opts.feature)) {\n fail(json, \"invalid-option\", \"--all validates everything; drop --service/--feature.\");\n return;\n }\n // Two targets is no target: silently validating only the feature taught\n // callers that --service had been honoured when it had been dropped.\n if (opts.service && opts.feature) {\n fail(json, \"invalid-option\", \"--service and --feature name different targets; pass one or the other.\");\n return;\n }\n // `--base` NARROWS a whole-fleet run; it does not name one. Beside a\n // positional, `--service` or `--feature` it is a scope over a scope — a\n // contradiction rather than a narrowing, whose \"this service if it\n // changed\" reading nobody could tell apart from a green over a service\n // never looked at. `--base` and not `--since`: `--base <ref>` is already\n // loam's word for a base git ref of the docs repo on diff and vouch.\n if (opts.base !== undefined && opts.all !== true) {\n fail(\n json,\n \"invalid-option\",\n target !== undefined || opts.service || opts.feature\n ? \"--base narrows --all to what changed since a ref; a named target is already a scope. Pass one or the other.\"\n : \"--base narrows --all to what changed since a ref — pass --all with it.\",\n );\n return;\n }\n\n const config = await configOrRefuse(json);\n if (config === null) return;\n const { docsDir } = config;\n // Before anything is enumerated: a docsDir that is not a docs repo must\n // refuse, not report an empty fleet. See docsRepoReady. `--feature` is the\n // one mode that never enumerates services, so it asks for less.\n if (!docsRepoReady(json, docsDir, opts.feature ? \"docs\" : \"services\")) return;\n // One invocation, one filesystem snapshot. Nothing is global: a later\n // command gets a fresh index and therefore cannot observe stale files.\n const fleet = new FleetContext();\n\n // `sources` are paths into a service's own repository, so they only mean\n // something when loam is standing in that repository — which is exactly\n // what loam.json's `service` records.\n const repoOf = (service: string): string | undefined =>\n config.service === service ? process.cwd() : undefined;\n // What loam itself wrote into that repository, which no `sources` list owes a reading of.\n const loamOwned = loamOwnedFiles(config);\n\n /**\n * One service target, from a name the enumeration or the grammar approved.\n * `land` is --all's landscape, read once for the whole run and handed to\n * the service. Null is PASSED — it means \"there is no landscape file\" —\n * so the test is `=== undefined`, never truthiness: a repo with no\n * landscape would otherwise re-load per service and double-report.\n *\n * `landscapeReported` says whether this run ALSO emits a landscape target\n * to carry the map's parse errors, and it is the caller's to state: a\n * preloaded map does not imply one. It used to be read off `land`, and\n * under `--base` the landscape is a target only when the branch changed\n * `architecture/` — so a broken committed map and a branch touching only\n * a service told that service \"reported once, on the landscape target\"\n * with no details, and the parser output was reported nowhere.\n */\n const serviceTarget = (id: RawServiceId, land?: LoadedDoc | null, landscapeReported = false): Promise<TargetReport> =>\n guarded({ kind: \"service\", id }, () =>\n validateService({\n docsDir, service: id, repoDir: repoOf(id), loamOwned, gherkinDir: config.gherkinDir, contracts: config.contracts, fleet,\n ...(land === undefined ? {} : { preloaded: land }),\n landscapeReported,\n }),\n );\n /** One feature target; `land` as above, and left undefined it means \"load it if you need it\". */\n const featureTarget = (feature: FeatureEntry, land?: LoadedDoc | null): Promise<TargetReport> =>\n guarded({ kind: \"feature\", id: feature.id }, () => validateFeature(docsDir, feature, land, fleet));\n\n const targets: TargetReport[] = [];\n /** What the positional argument turned out to name, for the JSON payload. */\n let resolvedKind: \"service\" | \"feature\" | undefined;\n /** The `--base` narrowing, once it is known. Null on every unscoped run — including every run without --base. */\n let scope: ValidateScope | null = null;\n\n try {\n if (opts.all) {\n // ONE workspace per document kind for the whole run — the block moved\n // to `fleet/load.ts` (`prefetchFleetDocuments`), which is where the\n // rule about which shape parses in which workspace belongs.\n const lp = landscapeFile(docsDir);\n const enumerated = await listServices(docsDir, fleet);\n const active = await listFeatures(docsDir, {}, fleet);\n // `--base` narrows the enumerations and nothing else: a kept target is\n // graded by exactly the checks `--all` would have run on it, at the\n // same severities, through the same `--strict`. It matches through the\n // ENUMERATIONS' own directories — changed-paths.ts says why a split\n // on \"/\" silently drops every filed service.\n if (opts.base !== undefined) {\n const narrowed = await scopeSince({ docsDir, ref: opts.base, services: enumerated, features: active });\n if (narrowed.kind === \"refused\") {\n fail(json, narrowed.code, narrowed.message);\n return;\n }\n scope = narrowed;\n }\n const services = scope === null ? enumerated : scope.services;\n const features = scope === null ? active : scope.features;\n await prefetchFleetDocuments({ docsDir, fleet, services, features });\n // Read whether or not the landscape is a TARGET: every service check\n // below joins against it, so a scoped run needs the map in hand even\n // when it does not grade the map itself. Through the fleet index, so\n // the single-service path — which now reads the same project — answers\n // out of the same memo rather than parsing it a second time.\n const land = existsSync(lp) ? await readLandscape(() => fleet.architecture(docsDir)) : null;\n // The fleet-level cross-check first: it frames everything below it, and a\n // service nobody drew is worth knowing before its own findings scroll past.\n // Under `--base` it is a target only when `architecture/` changed — the\n // landscape owns no service directory, so that is its whole footprint —\n // and even then not whole: `scope.ts` drops what grades a file the\n // branch left alone and adds the call edges it wrote.\n const landscapeIsTarget = scope === null || scope.landscape;\n if (landscapeIsTarget) {\n const landscape = await validateLandscape(docsDir, land, fleet);\n targets.push(landscape);\n // The agent contract check, --all only: AGENTS.md is written once and\n // never refreshed (the ownership contract), so the one thing the\n // docs-repo-wide mode owes it is detection — a stamp older than the\n // binary means agents are branching on tables the binary no longer\n // honours. It grades the repo, not any service, so it rides on the\n // landscape target.\n const agentsPath = agentsFile(docsDir);\n const agentsText = existsSync(agentsPath) ? await readFile(agentsPath, \"utf8\") : null;\n const agents = agentsStaleFinding(agentsText, LOAM_VERSION);\n if (agents !== null) landscape.findings.push(agents);\n // The other direction, and the one that changes what a PASS means:\n // this binary predates the corpus, so a directive it cannot parse is\n // read as prose and produces no join to fail.\n const behind = binaryBehindFinding(agentsText, LOAM_VERSION);\n if (behind !== null) landscape.findings.push(behind);\n if (scope !== null) {\n landscape.findings = await scopedLandscapeFindings(landscape.findings, {\n docsDir, scope, land, services: enumerated, fleet,\n });\n }\n }\n targets.push(...(await inOrder(services, (svc) => serviceTarget(svc.id, land, landscapeIsTarget))));\n targets.push(...(await inOrder(features, (feat) => featureTarget(feat, land))));\n } else if (opts.feature) {\n const feature = await resolveFeature(docsDir, opts.feature, \"exclude\", fleet);\n if (!feature) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, opts.feature, fleet));\n return;\n }\n targets.push(await featureTarget(feature));\n } else if (target !== undefined) {\n // The positional reads the way `show` reads one: try the feature first\n // (ids like FEAT-101 are distinctive, service names are arbitrary), then\n // the service. --service/--feature stay as the explicit spellings for a\n // name that could be both — and when both readings exist, the run says\n // so out loud instead of silently taking one (`target.ambiguous`).\n const feature = await resolveFeature(docsDir, target, \"exclude\", fleet);\n const isService = (await listServices(docsDir, fleet)).some((s) => s.id === target);\n if (feature) {\n resolvedKind = \"feature\";\n const report = await featureTarget(feature);\n if (isService) report.findings.unshift(ambiguousTarget(target, \"feature\"));\n targets.push(report);\n } else {\n // Neither reading exists. An archived feature is its own diagnosis\n // (\"already archived\", not \"no such thing\"); anything else reads as a\n // service, so the did-you-mean hints in service.unknown fire.\n if (!isService && (await resolveFeature(docsDir, target, \"only\", fleet)) !== null) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, target, fleet));\n return;\n }\n resolvedKind = \"service\";\n const resolved = await resolveServiceTarget(docsDir, target, \"target\", fleet);\n if (!resolved.ok) return fail(json, \"invalid-option\", resolved.problem);\n targets.push(await serviceTarget(resolved.id));\n }\n } else {\n const service = opts.service ?? config.service;\n if (!service) {\n fail(json, \"invalid-option\", NO_SERVICE_MESSAGE);\n return;\n }\n const resolved = await resolveServiceTarget(docsDir, service, \"--service\", fleet);\n if (!resolved.ok) return fail(json, \"invalid-option\", resolved.problem);\n targets.push(await serviceTarget(resolved.id));\n }\n } catch (err) {\n // The docs repo went away between the gate above and the enumeration.\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // An IO failure that `guarded` could not localise to one target — it\n // happened in the ENUMERATION, which reads every living spec's\n // frontmatter to build the service list. There is no partial report to\n // give: without the list, no service was checked, including the ones\n // that are fine. Say which file, and refuse; the one thing that must\n // not happen is a stack trace on stdout's sibling stream and an\n // `internal` envelope that names nothing.\n reportRepositoryUnavailable(\n json,\n err,\n \"the service list itself is unknown and nothing was validated\",\n docsDir,\n );\n return;\n }\n\n // By subject, not by finding — the rule lives with the rollup in report.ts.\n const unverifiable = unverifiableSubjects(targets);\n // A journal in the repo outranks everything graded above: the docs may\n // be half-merged, and in CI a green over them merges. Led, not appended\n // — it is the reason nothing below can be trusted — and attached to the\n // first target because a finding lives in one.\n const interrupted = await interruptedCommitFinding(docsDir);\n if (interrupted !== null && targets.length > 0) targets[0]!.findings.unshift(interrupted);\n\n // The fleet scorecard, --all only: recomputed per invocation off the same\n // memoized reads this run already made — loam stores no history — and it\n // fails closed per axis, never whole (fleet/scorecard/scorecard.ts).\n //\n // Suppressed under `--base`: its denominators are the graded targets, so\n // over a narrowed set it reads \"2 of 2 services documented\" for a fleet\n // where ten more are not — and its axes license report.ts's grouping,\n // which would drop real warnings because an unlooked-at axis reads zero.\n const scorecard =\n opts.all && scope === null\n ? await buildScorecard({ docsDir, targets, fleet, boundService: config.service })\n : null;\n\n const valid = reportValid(targets);\n const capped = targets.map(capDetails);\n if (json) {\n emitJson({\n command: \"validate\",\n valid,\n summary: summary(targets),\n ...(resolvedKind === undefined ? {} : { resolvedKind }),\n // What this run looked at. Only under `--base`, so an unscoped payload\n // is today's document and its ABSENCE says `summary` counts the whole\n // fleet — scopeJson has why a scoped one may not stay silent.\n ...(scope === null ? {} : { scope: scopeJson(scope) }),\n // Emitted in every mode now that it is derived per service: a\n // single-service run knows exactly as much about its own blind spot\n // as `--all` knows about the fleet's.\n sourcesUnverifiableFromHere: unverifiable,\n ...(scorecard === null ? {} : { scorecard }),\n // What to do about every code this run raised — the one thing the\n // payload could not previously answer. `validate` is step 1 of every\n // generated protocol and the command an agent reads first, and until\n // now it replied in prose: a finding names a code and says what is\n // wrong, and nothing in it said that `loam explain <code>` knows what\n // to do next.\n //\n // A top-level MAP, not a field on each finding, for the reason\n // report.ts's DETAIL_LIMIT gives about evidence: a hundred-service\n // fleet raising one code a hundred times would ship the same\n // sentence a hundred times, in the interface an agent pipes into a\n // context window. The consumer joins `findings[].code` to\n // `fixes[code]` — one lookup, no round trip, no repetition.\n //\n // Keyed ONLY by codes a non-`ok` finding raised. A confirmation has\n // nothing to fix, and on a clean fleet the `ok` codes are the\n // overwhelming majority of findings; attaching fixes to them would\n // pay the whole vocabulary's byte cost to tell a reader that nothing\n // is wrong. `targets` is read here rather than `capped` because\n // capping truncates evidence, not findings — the two raise the same\n // codes, and reading the uncapped set keeps this rollup computed off\n // the same targets as `summary()`.\n //\n // Not behind a flag, deliberately. It is additive, so no consumer\n // that ignores it is affected; it is small, because it carries only\n // the handful of codes this run actually raised rather than the\n // 227-row vocabulary; and a flag would mean an agent has to already\n // know the fixes exist in order to ask for them — which is the exact\n // gap being closed. It is emitted on a green run too, as `{}`, so a\n // consumer never has to branch on the key's presence before looking\n // a code up in it.\n fixes: fixesFor(\n targets.flatMap((t) => t.findings.filter((f) => f.severity !== \"ok\").map((f) => f.code)),\n ),\n targets: capped.map(targetJson),\n });\n } else {\n // The scope FIRST: a reader has to know what the report below is a\n // report ABOUT, and in the zero-target case there is nothing below at\n // all — printed after the findings, the one run that most needs words\n // would read as a green over the whole system.\n if (scope !== null) console.log(scopeLine(scope));\n renderText(capped, {\n all: opts.all === true,\n errorsOnly: opts.errorsOnly === true,\n unverifiable,\n scorecard,\n });\n if (scorecard !== null) printScorecard(scorecard);\n }\n // --strict is a per-invocation CI lever: it fails the run on any error or\n // warning (ok-severity confirmations never trip it — virtually every\n // clean target emits some), and changes nothing else — `valid` keeps meaning \"no\n // errors\" in text and JSON alike, so two pipelines reading the same repo\n // may grade the same report differently and both are telling the truth.\n const strictFailed =\n opts.strict === true &&\n countSeverity(targets, \"error\") + countSeverity(targets, \"warn\") > 0;\n if (!valid || strictFailed) process.exitCode = 1;\n });\n}\n"]}
@@ -22,7 +22,7 @@ import { readFile } from "node:fs/promises";
22
22
  import { resolve } from "node:path";
23
23
  import { emitJson, fail, repoPath } from "../../../core/envelope/json.js";
24
24
  import { isRecord } from "../../../core/kernel/records.js";
25
- import { checkAnswers } from "../../../core/verify/answers.js";
25
+ import { answerEntries, checkAnswers } from "../../../core/verify/answers.js";
26
26
  import {} from "../../../core/verify/checklist.js";
27
27
  import { crossExamine, disjointNotice } from "../../../core/verify/cross/diff.js";
28
28
  import { plural } from "../../policy/format.js";
@@ -38,11 +38,7 @@ import {} from "../report.js";
38
38
  function offLensNote(scope, raw) {
39
39
  if (scope.lens === undefined)
40
40
  return "";
41
- const entries = Array.isArray(raw)
42
- ? raw
43
- : isRecord(raw) && Array.isArray(raw["answers"])
44
- ? raw["answers"]
45
- : [];
41
+ const entries = answerEntries(raw) ?? [];
46
42
  const scoped = new Set(scope.scoped.map((c) => c.id));
47
43
  const onChecklist = new Set(scope.checklist.claims.map((c) => c.id));
48
44
  const foreign = [
@@ -1 +1 @@
1
- {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/commands/verify/cross/diff.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam verify <FEAT> --diff-answers a.json b.json` — the command surface of\n * the cross-examination lens.\n *\n * A third read form beside the report and frozen views: it takes no lock,\n * needs no service binding, and writes nothing. The payload deliberately\n * carries NO `verified`, `verdict` or `attested` key — their absence is the\n * doctrine made structural (`core/verify/cross/diff.ts` holds the\n * reasoning): this surface ranks review, it grades nothing.\n *\n * Both files validate through the same `checkAnswers` the record path uses,\n * against the same scoped checklist, with `runnerOwned` unset: under a diff\n * both agents answer everything in scope, `scenario.tested` included — that\n * is the attested channel, and this command records nothing. If a future\n * wave ever makes scenario claims runner-only even without `--results`,\n * blind agents can no longer answer them and this command starts refusing;\n * `checkAnswers`' own `runnerOwned` note carries the same warning where that\n * editor will see it.\n */\nimport { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { emitJson, fail, repoPath } from \"../../../core/envelope/json.js\";\nimport { isRecord } from \"../../../core/kernel/records.js\";\nimport { checkAnswers, type Answer, type AnswerRefusal } from \"../../../core/verify/answers.js\";\nimport { type Checklist, type Claim } from \"../../../core/verify/checklist.js\";\nimport { crossExamine, disjointNotice, type CrossAnswer } from \"../../../core/verify/cross/diff.js\";\nimport { plural } from \"../../policy/format.js\";\nimport { type VerifyTarget } from \"../report.js\";\n\n/**\n * The two files, named for the order they were given: `a` is the first and\n * every refusal about it says \"first answer set\". One record rather than two\n * loose strings because they are never asked about separately, and because a\n * transposed pair is exactly the mistake a positional form invites.\n */\nexport interface AnswerFiles {\n a: string;\n b: string;\n}\n\ntype SideRead =\n | { ok: true; answers: Answer[]; digest: string }\n | { ok: false; code: AnswerRefusal; message: string };\n\n/**\n * Everything a side is validated against: the lens-scoped claims, the whole\n * checklist behind them (what the honest off-lens diagnosis is written from),\n * and the lens itself. One record because the three are never asked about\n * separately — a scoped array without its checklist is how the false \"not on\n * the checklist\" diagnosis happened.\n */\ninterface DiffScope {\n scoped: Claim[];\n checklist: Checklist;\n lens?: string;\n}\n\n/**\n * The honest half of a lens-narrowed mismatch. `checkAnswers` is handed only\n * the scoped claims, so an answer naming another service's claim — which IS\n * on the feature's checklist — would otherwise be called \"not on the\n * checklist\": the same false diagnosis `runnerOwned` exists to prevent on the\n * recording path. When the stray ids are real claims the lens filtered out,\n * say that, and say what the lens narrowed.\n */\nfunction offLensNote(scope: DiffScope, raw: unknown): string {\n if (scope.lens === undefined) return \"\";\n const entries = Array.isArray(raw)\n ? raw\n : isRecord(raw) && Array.isArray(raw[\"answers\"])\n ? (raw[\"answers\"] as unknown[])\n : [];\n const scoped = new Set(scope.scoped.map((c) => c.id));\n const onChecklist = new Set(scope.checklist.claims.map((c) => c.id));\n const foreign = [\n ...new Set(\n entries\n .filter(isRecord)\n .map((entry) => entry[\"id\"])\n .filter((id): id is string => typeof id === \"string\" && onChecklist.has(id) && !scoped.has(id)),\n ),\n ];\n if (foreign.length === 0) return \"\";\n return (\n ` (${foreign.join(\", \")}: on the ${scope.checklist.feature} checklist but owned by another service — ` +\n `--service ${scope.lens} narrowed the comparison to ${scope.scoped.length} of ${scope.checklist.claims.length} claims)`\n );\n}\n\n/**\n * Read, digest and validate ONE answer set. Fail-closed on purpose at every\n * step: an unreadable or malformed file is a refusal naming which file and\n * why — never a silently empty side, which would grade as \"no disagreement\"\n * about claims nobody compared.\n */\nasync function readSide(scope: DiffScope, file: string, side: \"first\" | \"second\"): Promise<SideRead> {\n const label = `${side} answer set (${file})`;\n let bytes: Buffer;\n try {\n // cwd-relative deliberately — record.ts's reason for the same choice: the\n // answer sets are the caller's hand-offs, not repository artifacts, and\n // nothing on any record will ever point at them.\n bytes = await readFile(resolve(process.cwd(), file));\n } catch (err) {\n return {\n ok: false,\n code: \"answers-unreadable\",\n message: `${label}: cannot read: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n let raw: unknown;\n try {\n raw = JSON.parse(bytes.toString(\"utf8\"));\n } catch (err) {\n return {\n ok: false,\n code: \"answers-unreadable\",\n message: `${label}: not JSON: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n const context = { feature: scope.checklist.feature, ...(scope.lens === undefined ? {} : { service: scope.lens }) };\n const checked = checkAnswers(scope.scoped, raw, undefined, context);\n if (!checked.ok) {\n return { ok: false, code: checked.code, message: `${label}: ${checked.message}${offLensNote(scope, raw)}` };\n }\n // The digest is over the exact bytes read — the same reviewer-checkable\n // promise `reportLine` makes for consumed reports: `shasum -a 256 <file>`\n // answers whether the diff in hand read the file in hand.\n return { ok: true, answers: checked.answers, digest: createHash(\"sha256\").update(bytes).digest(\"hex\") };\n}\n\nfunction sideLine(answer: CrossAnswer): string {\n if (answer.verdict === \"confirmed\") return `confirmed (${answer.evidence.join(\", \")})`;\n return `unconfirmed${answer.note === undefined ? \"\" : ` — ${answer.note}`}`;\n}\n\nexport async function diffAnswers(\n target: VerifyTarget,\n checklist: Checklist,\n files: AnswerFiles,\n scope: { lens?: string },\n): Promise<void> {\n const { docsDir, featureDir, json } = target;\n const lens = scope.lens;\n // The same lens the read view applies (report.ts): the claims that\n // service's code answers, no repository binding asked for — a diff writes\n // nothing, so there is nothing to bind.\n const scoped = lens === undefined ? checklist.claims : checklist.claims.filter((c) => c.subject === lens);\n // record.ts's refusal, mirrored: a lens that owns nothing here is almost\n // always a typo'd service id, and validating two files against an empty\n // claim set would call every honest answer off-checklist instead of naming\n // the real mistake.\n if (lens !== undefined && scoped.length === 0) {\n return fail(\n json,\n \"unknown-service\",\n `The current ${checklist.feature} checklist has no claims owned by service '${lens}'.`,\n );\n }\n const diffScope: DiffScope = { scoped, checklist, ...(lens === undefined ? {} : { lens }) };\n\n // Sequential on purpose — a short-circuit, not a race: readSide never\n // rejects, but when the first file refuses, the second is never read or\n // parsed, so the refusal a caller sees always names the earliest problem\n // and no work is spent on a file whose sibling already failed the run.\n const a = await readSide(diffScope, files.a, \"first\");\n if (!a.ok) return fail(json, a.code, a.message);\n const b = await readSide(diffScope, files.b, \"second\");\n if (!b.ok) return fail(json, b.code, b.message);\n\n const cross = crossExamine(scoped, a.answers, b.answers);\n const notice = disjointNotice(cross.rows);\n\n if (json) {\n emitJson({\n command: \"verify\",\n feature: checklist.feature,\n path: repoPath(docsDir, featureDir),\n digest: checklist.digest,\n // Named only when narrowed — report.ts's rule: a consumer must never\n // mistake one service's cross-examination for the feature's.\n ...(lens === undefined ? {} : { service: lens, checklistClaims: checklist.claims.length }),\n files: {\n a: { path: files.a, digest: a.digest },\n b: { path: files.b, digest: b.digest },\n },\n summary: cross.summary,\n claims: cross.rows,\n disagreements: cross.disagreements,\n ...(notice === null ? {} : { notices: [notice] }),\n });\n return;\n }\n\n const scopeNote =\n lens === undefined ? \"\" : ` owned by ${lens} (of ${plural(checklist.claims.length, \"claim\")} on the checklist)`;\n console.log(\n `${checklist.feature} — cross-examination over ${plural(cross.rows.length, \"claim\")}${scopeNote} (${files.a} vs ${files.b})\\n`,\n );\n if (cross.rows.length === 0) {\n // Only the whole-checklist form can get here: a lens that owns nothing\n // refused `unknown-service` above.\n console.log(\" Nothing to cross-examine: this feature's delta, specs and openapi promise nothing yet.\");\n return;\n }\n for (const row of cross.rows) {\n if (row.code === \"cross.disagree\") {\n console.log(` ✗ cross.disagree ${row.id} [${row.subject}] ${row.claim}`);\n console.log(` a: ${sideLine(row.a)}`);\n console.log(` b: ${sideLine(row.b)}`);\n } else if (row.code === \"cross.agree-unconfirmed\") {\n console.log(` = ${row.id} [${row.subject}] ${row.claim} (both unconfirmed)`);\n } else {\n console.log(` = ${row.id} [${row.subject}] ${row.claim}${row.evidenceDisjoint === true ? \" (disjoint evidence)\" : \"\"}`);\n }\n }\n console.log(\"\");\n if (notice !== null) console.log(` ⚠ ${notice.code}: ${notice.message}`);\n console.log(\n ` ${cross.summary.agreeConfirmed} agree-confirmed, ${cross.summary.agreeUnconfirmed} agree-unconfirmed, ${cross.summary.disagree} disagree.`,\n );\n console.log(\"\\n Agreement is not verification — nothing was written and no verdict changed.\");\n console.log(\" Read the disagreements first, then the disjoint-evidence agreements.\");\n}\n"]}
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/commands/verify/cross/diff.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam verify <FEAT> --diff-answers a.json b.json` — the command surface of\n * the cross-examination lens.\n *\n * A third read form beside the report and frozen views: it takes no lock,\n * needs no service binding, and writes nothing. The payload deliberately\n * carries NO `verified`, `verdict` or `attested` key — their absence is the\n * doctrine made structural (`core/verify/cross/diff.ts` holds the\n * reasoning): this surface ranks review, it grades nothing.\n *\n * Both files validate through the same `checkAnswers` the record path uses,\n * against the same scoped checklist, with `runnerOwned` unset: under a diff\n * both agents answer everything in scope, `scenario.tested` included — that\n * is the attested channel, and this command records nothing. If a future\n * wave ever makes scenario claims runner-only even without `--results`,\n * blind agents can no longer answer them and this command starts refusing;\n * `checkAnswers`' own `runnerOwned` note carries the same warning where that\n * editor will see it.\n */\nimport { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { emitJson, fail, repoPath } from \"../../../core/envelope/json.js\";\nimport { isRecord } from \"../../../core/kernel/records.js\";\nimport { answerEntries, checkAnswers, type Answer, type AnswerRefusal } from \"../../../core/verify/answers.js\";\nimport { type Checklist, type Claim } from \"../../../core/verify/checklist.js\";\nimport { crossExamine, disjointNotice, type CrossAnswer } from \"../../../core/verify/cross/diff.js\";\nimport { plural } from \"../../policy/format.js\";\nimport { type VerifyTarget } from \"../report.js\";\n\n/**\n * The two files, named for the order they were given: `a` is the first and\n * every refusal about it says \"first answer set\". One record rather than two\n * loose strings because they are never asked about separately, and because a\n * transposed pair is exactly the mistake a positional form invites.\n */\nexport interface AnswerFiles {\n a: string;\n b: string;\n}\n\ntype SideRead =\n | { ok: true; answers: Answer[]; digest: string }\n | { ok: false; code: AnswerRefusal; message: string };\n\n/**\n * Everything a side is validated against: the lens-scoped claims, the whole\n * checklist behind them (what the honest off-lens diagnosis is written from),\n * and the lens itself. One record because the three are never asked about\n * separately — a scoped array without its checklist is how the false \"not on\n * the checklist\" diagnosis happened.\n */\ninterface DiffScope {\n scoped: Claim[];\n checklist: Checklist;\n lens?: string;\n}\n\n/**\n * The honest half of a lens-narrowed mismatch. `checkAnswers` is handed only\n * the scoped claims, so an answer naming another service's claim — which IS\n * on the feature's checklist — would otherwise be called \"not on the\n * checklist\": the same false diagnosis `runnerOwned` exists to prevent on the\n * recording path. When the stray ids are real claims the lens filtered out,\n * say that, and say what the lens narrowed.\n */\nfunction offLensNote(scope: DiffScope, raw: unknown): string {\n if (scope.lens === undefined) return \"\";\n const entries = answerEntries(raw) ?? [];\n const scoped = new Set(scope.scoped.map((c) => c.id));\n const onChecklist = new Set(scope.checklist.claims.map((c) => c.id));\n const foreign = [\n ...new Set(\n entries\n .filter(isRecord)\n .map((entry) => entry[\"id\"])\n .filter((id): id is string => typeof id === \"string\" && onChecklist.has(id) && !scoped.has(id)),\n ),\n ];\n if (foreign.length === 0) return \"\";\n return (\n ` (${foreign.join(\", \")}: on the ${scope.checklist.feature} checklist but owned by another service — ` +\n `--service ${scope.lens} narrowed the comparison to ${scope.scoped.length} of ${scope.checklist.claims.length} claims)`\n );\n}\n\n/**\n * Read, digest and validate ONE answer set. Fail-closed on purpose at every\n * step: an unreadable or malformed file is a refusal naming which file and\n * why — never a silently empty side, which would grade as \"no disagreement\"\n * about claims nobody compared.\n */\nasync function readSide(scope: DiffScope, file: string, side: \"first\" | \"second\"): Promise<SideRead> {\n const label = `${side} answer set (${file})`;\n let bytes: Buffer;\n try {\n // cwd-relative deliberately — record.ts's reason for the same choice: the\n // answer sets are the caller's hand-offs, not repository artifacts, and\n // nothing on any record will ever point at them.\n bytes = await readFile(resolve(process.cwd(), file));\n } catch (err) {\n return {\n ok: false,\n code: \"answers-unreadable\",\n message: `${label}: cannot read: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n let raw: unknown;\n try {\n raw = JSON.parse(bytes.toString(\"utf8\"));\n } catch (err) {\n return {\n ok: false,\n code: \"answers-unreadable\",\n message: `${label}: not JSON: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n const context = { feature: scope.checklist.feature, ...(scope.lens === undefined ? {} : { service: scope.lens }) };\n const checked = checkAnswers(scope.scoped, raw, undefined, context);\n if (!checked.ok) {\n return { ok: false, code: checked.code, message: `${label}: ${checked.message}${offLensNote(scope, raw)}` };\n }\n // The digest is over the exact bytes read — the same reviewer-checkable\n // promise `reportLine` makes for consumed reports: `shasum -a 256 <file>`\n // answers whether the diff in hand read the file in hand.\n return { ok: true, answers: checked.answers, digest: createHash(\"sha256\").update(bytes).digest(\"hex\") };\n}\n\nfunction sideLine(answer: CrossAnswer): string {\n if (answer.verdict === \"confirmed\") return `confirmed (${answer.evidence.join(\", \")})`;\n return `unconfirmed${answer.note === undefined ? \"\" : ` — ${answer.note}`}`;\n}\n\nexport async function diffAnswers(\n target: VerifyTarget,\n checklist: Checklist,\n files: AnswerFiles,\n scope: { lens?: string },\n): Promise<void> {\n const { docsDir, featureDir, json } = target;\n const lens = scope.lens;\n // The same lens the read view applies (report.ts): the claims that\n // service's code answers, no repository binding asked for — a diff writes\n // nothing, so there is nothing to bind.\n const scoped = lens === undefined ? checklist.claims : checklist.claims.filter((c) => c.subject === lens);\n // record.ts's refusal, mirrored: a lens that owns nothing here is almost\n // always a typo'd service id, and validating two files against an empty\n // claim set would call every honest answer off-checklist instead of naming\n // the real mistake.\n if (lens !== undefined && scoped.length === 0) {\n return fail(\n json,\n \"unknown-service\",\n `The current ${checklist.feature} checklist has no claims owned by service '${lens}'.`,\n );\n }\n const diffScope: DiffScope = { scoped, checklist, ...(lens === undefined ? {} : { lens }) };\n\n // Sequential on purpose — a short-circuit, not a race: readSide never\n // rejects, but when the first file refuses, the second is never read or\n // parsed, so the refusal a caller sees always names the earliest problem\n // and no work is spent on a file whose sibling already failed the run.\n const a = await readSide(diffScope, files.a, \"first\");\n if (!a.ok) return fail(json, a.code, a.message);\n const b = await readSide(diffScope, files.b, \"second\");\n if (!b.ok) return fail(json, b.code, b.message);\n\n const cross = crossExamine(scoped, a.answers, b.answers);\n const notice = disjointNotice(cross.rows);\n\n if (json) {\n emitJson({\n command: \"verify\",\n feature: checklist.feature,\n path: repoPath(docsDir, featureDir),\n digest: checklist.digest,\n // Named only when narrowed — report.ts's rule: a consumer must never\n // mistake one service's cross-examination for the feature's.\n ...(lens === undefined ? {} : { service: lens, checklistClaims: checklist.claims.length }),\n files: {\n a: { path: files.a, digest: a.digest },\n b: { path: files.b, digest: b.digest },\n },\n summary: cross.summary,\n claims: cross.rows,\n disagreements: cross.disagreements,\n ...(notice === null ? {} : { notices: [notice] }),\n });\n return;\n }\n\n const scopeNote =\n lens === undefined ? \"\" : ` owned by ${lens} (of ${plural(checklist.claims.length, \"claim\")} on the checklist)`;\n console.log(\n `${checklist.feature} — cross-examination over ${plural(cross.rows.length, \"claim\")}${scopeNote} (${files.a} vs ${files.b})\\n`,\n );\n if (cross.rows.length === 0) {\n // Only the whole-checklist form can get here: a lens that owns nothing\n // refused `unknown-service` above.\n console.log(\" Nothing to cross-examine: this feature's delta, specs and openapi promise nothing yet.\");\n return;\n }\n for (const row of cross.rows) {\n if (row.code === \"cross.disagree\") {\n console.log(` ✗ cross.disagree ${row.id} [${row.subject}] ${row.claim}`);\n console.log(` a: ${sideLine(row.a)}`);\n console.log(` b: ${sideLine(row.b)}`);\n } else if (row.code === \"cross.agree-unconfirmed\") {\n console.log(` = ${row.id} [${row.subject}] ${row.claim} (both unconfirmed)`);\n } else {\n console.log(` = ${row.id} [${row.subject}] ${row.claim}${row.evidenceDisjoint === true ? \" (disjoint evidence)\" : \"\"}`);\n }\n }\n console.log(\"\");\n if (notice !== null) console.log(` ⚠ ${notice.code}: ${notice.message}`);\n console.log(\n ` ${cross.summary.agreeConfirmed} agree-confirmed, ${cross.summary.agreeUnconfirmed} agree-unconfirmed, ${cross.summary.disagree} disagree.`,\n );\n console.log(\"\\n Agreement is not verification — nothing was written and no verdict changed.\");\n console.log(\" Read the disagreements first, then the disjoint-evidence agreements.\");\n}\n"]}
@@ -9,7 +9,7 @@ import { readContractReport } from "../../../core/verify/evidence/contract.js";
9
9
  import {} from "../../../core/verify/record.js";
10
10
  import { readReportArtifact } from "./read.js";
11
11
  export async function readContractResults(spelled, repoDir) {
12
- const artifact = await readReportArtifact(spelled, repoDir, "contract-test report");
12
+ const artifact = await readReportArtifact(spelled, repoDir, "contract-test report", 'It reads loam\'s generic contract-results JSON — {"loamContractReport": 1, "results": [{"operationId": "…", "status": "passed"}]} — which any contract suite can be adapted into.');
13
13
  if (!artifact.ok)
14
14
  return artifact;
15
15
  const parsed = readContractReport(artifact.doc, spelled);
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../../src/commands/verify/evidence/contract.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAoB,MAAM,2CAA2C,CAAC;AACjG,OAAO,EAA+B,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAM/C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAe,EACf,OAA2B;IAE3B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;IACpF,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAClC,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1F,OAAO;QACL,EAAE,EAAE,IAAI;QACR,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ,CAAC,OAAO;YACtB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,sEAAsE;YACtE,iEAAiE;YACjE,UAAU,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;YAC/D,MAAM,EAAE,SAAS;SAClB;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * `--contract-results` consumed: the file read and pinned through the shared\n * plumbing in `./read.ts`, then graded by the format reader in\n * `core/verify/evidence/contract.ts`. Mirrors what `../results.ts` is for the\n * cucumber report — the command-side half that touches the filesystem, kept\n * apart from the core half that only ever judges a parsed document.\n */\nimport { readContractReport, type ContractRun } from \"../../../core/verify/evidence/contract.js\";\nimport { type ConsumedContractReport } from \"../../../core/verify/record.js\";\nimport { readReportArtifact } from \"./read.js\";\n\nexport type ContractResultsRead =\n | { ok: true; report: ConsumedContractReport; runs: ContractRun[] }\n | { ok: false; code: \"answers-unreadable\"; message: string };\n\nexport async function readContractResults(\n spelled: string,\n repoDir: string | undefined,\n): Promise<ContractResultsRead> {\n const artifact = await readReportArtifact(spelled, repoDir, \"contract-test report\");\n if (!artifact.ok) return artifact;\n const parsed = readContractReport(artifact.doc, spelled);\n if (!parsed.ok) return { ok: false, code: \"answers-unreadable\", message: parsed.message };\n return {\n ok: true,\n runs: parsed.runs,\n report: {\n path: artifact.spelled,\n digest: artifact.digest,\n mtime: artifact.mtime,\n // Distinct operationIds, not entries: the pin says how much of an API\n // the file speaks about, and a re-run is not a second operation.\n operations: new Set(parsed.runs.map((r) => r.operationId)).size,\n format: \"generic\",\n },\n };\n}\n"]}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../../src/commands/verify/evidence/contract.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAoB,MAAM,2CAA2C,CAAC;AACjG,OAAO,EAA+B,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAM/C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAAe,EACf,OAA2B;IAE3B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CACvC,OAAO,EACP,OAAO,EACP,sBAAsB,EACtB,mLAAmL,CACpL,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAClC,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1F,OAAO;QACL,EAAE,EAAE,IAAI;QACR,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ,CAAC,OAAO;YACtB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,sEAAsE;YACtE,iEAAiE;YACjE,UAAU,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;YAC/D,MAAM,EAAE,SAAS;SAClB;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * `--contract-results` consumed: the file read and pinned through the shared\n * plumbing in `./read.ts`, then graded by the format reader in\n * `core/verify/evidence/contract.ts`. Mirrors what `../results.ts` is for the\n * cucumber report — the command-side half that touches the filesystem, kept\n * apart from the core half that only ever judges a parsed document.\n */\nimport { readContractReport, type ContractRun } from \"../../../core/verify/evidence/contract.js\";\nimport { type ConsumedContractReport } from \"../../../core/verify/record.js\";\nimport { readReportArtifact } from \"./read.js\";\n\nexport type ContractResultsRead =\n | { ok: true; report: ConsumedContractReport; runs: ContractRun[] }\n | { ok: false; code: \"answers-unreadable\"; message: string };\n\nexport async function readContractResults(\n spelled: string,\n repoDir: string | undefined,\n): Promise<ContractResultsRead> {\n const artifact = await readReportArtifact(\n spelled,\n repoDir,\n \"contract-test report\",\n 'It reads loam\\'s generic contract-results JSON — {\"loamContractReport\": 1, \"results\": [{\"operationId\": \"…\", \"status\": \"passed\"}]} — which any contract suite can be adapted into.',\n );\n if (!artifact.ok) return artifact;\n const parsed = readContractReport(artifact.doc, spelled);\n if (!parsed.ok) return { ok: false, code: \"answers-unreadable\", message: parsed.message };\n return {\n ok: true,\n runs: parsed.runs,\n report: {\n path: artifact.spelled,\n digest: artifact.digest,\n mtime: artifact.mtime,\n // Distinct operationIds, not entries: the pin says how much of an API\n // the file speaks about, and a re-run is not a second operation.\n operations: new Set(parsed.runs.map((r) => r.operationId)).size,\n format: \"generic\",\n },\n };\n}\n"]}
@@ -27,7 +27,14 @@ import { createHash } from "node:crypto";
27
27
  import { readFile, stat } from "node:fs/promises";
28
28
  import { resolve } from "node:path";
29
29
  import { resolvePortableFileInside } from "../../../core/kernel/path-safety.js";
30
- export async function readReportArtifact(spelled, repoDir, label) {
30
+ /**
31
+ * `shapes` is the sentence naming what the flag accepts, appended when the file
32
+ * is not JSON at all. A JUnit XML report handed to `--results` used to die
33
+ * with the parser's own "Unexpected token '<' … is not valid JSON" — true, and
34
+ * no help to somebody whose runner speaks XML: the refusal has to say which
35
+ * shapes ARE read, because one of them exists precisely for that runner.
36
+ */
37
+ export async function readReportArtifact(spelled, repoDir, label, shapes) {
31
38
  const refuse = (message) => ({ ok: false, code: "answers-unreadable", message });
32
39
  let path;
33
40
  if (repoDir === undefined) {
@@ -55,11 +62,13 @@ export async function readReportArtifact(spelled, repoDir, label) {
55
62
  return refuse(`Cannot read ${spelled} as a ${label}: the file is not valid UTF-8`);
56
63
  }
57
64
  let doc;
65
+ const text = bytes.toString("utf8");
58
66
  try {
59
- doc = JSON.parse(bytes.toString("utf8"));
67
+ doc = JSON.parse(text);
60
68
  }
61
69
  catch (err) {
62
- return refuse(`Cannot read ${spelled} as a ${label}: ${err instanceof Error ? err.message : String(err)}`);
70
+ const what = text.trimStart().startsWith("<") ? "it is XML (a JUnit report?), not JSON" : "it is not JSON";
71
+ return refuse(`Cannot read ${spelled} as a ${label}: ${what} (${err instanceof Error ? err.message : String(err)}). ${shapes}`);
63
72
  }
64
73
  return {
65
74
  ok: true,
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../../src/commands/verify/evidence/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAgBhF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAe,EACf,OAA2B,EAC3B,KAAa;IAEb,MAAM,MAAM,GAAG,CAAC,OAAe,EAAsB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7G,IAAI,IAAY,CAAC;IACjB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,IAAI,GAAG,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,MAAM,CACX,sBAAsB,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI;gBACpF,uHAAuH,CAC1H,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAa,CAAC;IAClB,IAAI,KAAW,CAAC;IAChB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,eAAe,OAAO,SAAS,KAAK,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,eAAe,OAAO,SAAS,KAAK,+BAA+B,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,eAAe,OAAO,SAAS,KAAK,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO;QACL,EAAE,EAAE,IAAI;QACR,OAAO;QACP,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACxD,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE;QAC1B,GAAG;KACJ,CAAC;AACJ,CAAC","sourcesContent":["/**\n * A report file as an artifact: resolved, read, digested, parsed — the\n * plumbing `--results` and `--contract-results` share, extracted from\n * `../results.ts` when the contract path became its second consumer. One\n * spelling on purpose: the two flags must agree byte-for-byte about what \"the\n * file loam read\" means, because both pins end up on the same record making\n * the same promise.\n *\n * `repoDir` is set in federated mode, and there the report must be a file\n * INSIDE the repository being attested, resolved by the same rules as evidence\n * (`portablePathOf` then `resolveInside`): an attestation says \"at this\n * commit, in this repository\", and a report living somewhere else answers for\n * a run nobody standing here can find. The legacy all-at-once form binds to no\n * repository at all, so it takes the path as spelled — its looser contract,\n * unchanged.\n *\n * Every refusal is `answers-unreadable` with the failing fact named — a path\n * that is a directory arrives here as the read's own EISDIR, a file that is\n * not UTF-8 is refused by name rather than parsed through U+FFFD substitution\n * (JSON.parse's \"unexpected token\" over bytes nobody wrote is a message that\n * sends the caller hunting a typo that is not there), and broken JSON carries\n * the parser's message. Never `internal`: the file is the caller's, so the\n * refusal must name the file.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { resolvePortableFileInside } from \"../../../core/kernel/path-safety.js\";\n\nexport type ReportArtifactRead =\n | {\n ok: true;\n /** The path as the caller spelled it — what goes on the record. */\n spelled: string;\n /** Full sha256 of the exact bytes read. */\n digest: string;\n /** ISO-8601 mtime of the report file. */\n mtime: string;\n /** The parsed JSON document, for the format reader to grade. */\n doc: unknown;\n }\n | { ok: false; code: \"answers-unreadable\"; message: string };\n\nexport async function readReportArtifact(\n spelled: string,\n repoDir: string | undefined,\n label: string,\n): Promise<ReportArtifactRead> {\n const refuse = (message: string): ReportArtifactRead => ({ ok: false, code: \"answers-unreadable\", message });\n let path: string;\n if (repoDir === undefined) {\n path = resolve(process.cwd(), spelled);\n } else {\n try {\n path = resolvePortableFileInside(repoDir, spelled, label);\n } catch (err) {\n return refuse(\n `Cannot answer from ${spelled}: ${err instanceof Error ? err.message : String(err)}. ` +\n \"A federated attestation rests on a report inside the repository it attests — give the path relative to the repo root.\",\n );\n }\n }\n\n let bytes: Buffer;\n let mtime: Date;\n try {\n bytes = await readFile(path);\n mtime = (await stat(path)).mtime;\n } catch (err) {\n return refuse(`Cannot read ${spelled} as a ${label}: ${err instanceof Error ? err.message : String(err)}`);\n }\n if (!isUtf8(bytes)) {\n return refuse(`Cannot read ${spelled} as a ${label}: the file is not valid UTF-8`);\n }\n let doc: unknown;\n try {\n doc = JSON.parse(bytes.toString(\"utf8\"));\n } catch (err) {\n return refuse(`Cannot read ${spelled} as a ${label}: ${err instanceof Error ? err.message : String(err)}`);\n }\n return {\n ok: true,\n spelled,\n digest: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n mtime: mtime.toISOString(),\n doc,\n };\n}\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../../src/commands/verify/evidence/read.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A report file as an artifact: resolved, read, digested, parsed — the\n * plumbing `--results` and `--contract-results` share, extracted from\n * `../results.ts` when the contract path became its second consumer. One\n * spelling on purpose: the two flags must agree byte-for-byte about what \"the\n * file loam read\" means, because both pins end up on the same record making\n * the same promise.\n *\n * `repoDir` is set in federated mode, and there the report must be a file\n * INSIDE the repository being attested, resolved by the same rules as evidence\n * (`portablePathOf` then `resolveInside`): an attestation says \"at this\n * commit, in this repository\", and a report living somewhere else answers for\n * a run nobody standing here can find. The legacy all-at-once form binds to no\n * repository at all, so it takes the path as spelled — its looser contract,\n * unchanged.\n *\n * Every refusal is `answers-unreadable` with the failing fact named — a path\n * that is a directory arrives here as the read's own EISDIR, a file that is\n * not UTF-8 is refused by name rather than parsed through U+FFFD substitution\n * (JSON.parse's \"unexpected token\" over bytes nobody wrote is a message that\n * sends the caller hunting a typo that is not there), and broken JSON carries\n * the parser's message. Never `internal`: the file is the caller's, so the\n * refusal must name the file.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { resolvePortableFileInside } from \"../../../core/kernel/path-safety.js\";\n\nexport type ReportArtifactRead =\n | {\n ok: true;\n /** The path as the caller spelled it — what goes on the record. */\n spelled: string;\n /** Full sha256 of the exact bytes read. */\n digest: string;\n /** ISO-8601 mtime of the report file. */\n mtime: string;\n /** The parsed JSON document, for the format reader to grade. */\n doc: unknown;\n }\n | { ok: false; code: \"answers-unreadable\"; message: string };\n\n/**\n * `shapes` is the sentence naming what the flag accepts, appended when the file\n * is not JSON at all. A JUnit XML report handed to `--results` used to die\n * with the parser's own \"Unexpected token '<' … is not valid JSON\" — true, and\n * no help to somebody whose runner speaks XML: the refusal has to say which\n * shapes ARE read, because one of them exists precisely for that runner.\n */\nexport async function readReportArtifact(\n spelled: string,\n repoDir: string | undefined,\n label: string,\n shapes: string,\n): Promise<ReportArtifactRead> {\n const refuse = (message: string): ReportArtifactRead => ({ ok: false, code: \"answers-unreadable\", message });\n let path: string;\n if (repoDir === undefined) {\n path = resolve(process.cwd(), spelled);\n } else {\n try {\n path = resolvePortableFileInside(repoDir, spelled, label);\n } catch (err) {\n return refuse(\n `Cannot answer from ${spelled}: ${err instanceof Error ? err.message : String(err)}. ` +\n \"A federated attestation rests on a report inside the repository it attests — give the path relative to the repo root.\",\n );\n }\n }\n\n let bytes: Buffer;\n let mtime: Date;\n try {\n bytes = await readFile(path);\n mtime = (await stat(path)).mtime;\n } catch (err) {\n return refuse(`Cannot read ${spelled} as a ${label}: ${err instanceof Error ? err.message : String(err)}`);\n }\n if (!isUtf8(bytes)) {\n return refuse(`Cannot read ${spelled} as a ${label}: the file is not valid UTF-8`);\n }\n let doc: unknown;\n const text = bytes.toString(\"utf8\");\n try {\n doc = JSON.parse(text);\n } catch (err) {\n const what = text.trimStart().startsWith(\"<\") ? \"it is XML (a JUnit report?), not JSON\" : \"it is not JSON\";\n return refuse(\n `Cannot read ${spelled} as a ${label}: ${what} (${err instanceof Error ? err.message : String(err)}). ${shapes}`,\n );\n }\n return {\n ok: true,\n spelled,\n digest: createHash(\"sha256\").update(bytes).digest(\"hex\"),\n mtime: mtime.toISOString(),\n doc,\n };\n}\n"]}