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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (901) hide show
  1. package/CHANGELOG.md +3448 -0
  2. package/COMPARISON.md +91 -3
  3. package/CONTRIBUTING.md +37 -0
  4. package/MIGRATING-from-OpenSpec.md +47 -23
  5. package/QUICKSTART.md +189 -12
  6. package/README.md +274 -65
  7. package/ROADMAP.md +572 -31
  8. package/SCHEMA.md +1709 -155
  9. package/WORKFLOW.md +298 -72
  10. package/dist/cli.js +26 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/adopt/adopt.js +5 -10
  13. package/dist/commands/adopt/adopt.js.map +1 -1
  14. package/dist/commands/adopt/render.js +50 -9
  15. package/dist/commands/adopt/render.js.map +1 -1
  16. package/dist/commands/archive/archive.js +16 -19
  17. package/dist/commands/archive/archive.js.map +1 -1
  18. package/dist/commands/archive/closing.js +42 -0
  19. package/dist/commands/archive/closing.js.map +1 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
  21. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
  22. package/dist/commands/archive/plan/contracts/consumers.js +56 -0
  23. package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
  24. package/dist/commands/archive/plan/contracts/openapi.js +77 -43
  25. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
  26. package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
  27. package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
  28. package/dist/commands/archive/plan/document/head.js +151 -0
  29. package/dist/commands/archive/plan/document/head.js.map +1 -0
  30. package/dist/commands/archive/plan/document/links.js +284 -0
  31. package/dist/commands/archive/plan/document/links.js.map +1 -0
  32. package/dist/commands/archive/plan/document/run.js +136 -0
  33. package/dist/commands/archive/plan/document/run.js.map +1 -0
  34. package/dist/commands/archive/plan/gate.js +21 -19
  35. package/dist/commands/archive/plan/gate.js.map +1 -1
  36. package/dist/commands/archive/plan/landscape.js +167 -30
  37. package/dist/commands/archive/plan/landscape.js.map +1 -1
  38. package/dist/commands/archive/plan/model/extending.js +85 -0
  39. package/dist/commands/archive/plan/model/extending.js.map +1 -0
  40. package/dist/commands/archive/plan/refusal.js +14 -19
  41. package/dist/commands/archive/plan/refusal.js.map +1 -1
  42. package/dist/commands/archive/plan/specs.js +137 -56
  43. package/dist/commands/archive/plan/specs.js.map +1 -1
  44. package/dist/commands/archive/plan/state.js +3 -0
  45. package/dist/commands/archive/plan/state.js.map +1 -1
  46. package/dist/commands/archive/run.js +134 -30
  47. package/dist/commands/archive/run.js.map +1 -1
  48. package/dist/commands/archive/shipped.js +49 -0
  49. package/dist/commands/archive/shipped.js.map +1 -0
  50. package/dist/commands/context/context.js +85 -18
  51. package/dist/commands/context/context.js.map +1 -1
  52. package/dist/commands/context/print.js +29 -7
  53. package/dist/commands/context/print.js.map +1 -1
  54. package/dist/commands/delta/delta.js +102 -14
  55. package/dist/commands/delta/delta.js.map +1 -1
  56. package/dist/commands/delta/print.js +17 -1
  57. package/dist/commands/delta/print.js.map +1 -1
  58. package/dist/commands/dependencies.js +3 -7
  59. package/dist/commands/dependencies.js.map +1 -1
  60. package/dist/commands/diff/diff.js +20 -27
  61. package/dist/commands/diff/diff.js.map +1 -1
  62. package/dist/commands/doctor.js +60 -1
  63. package/dist/commands/doctor.js.map +1 -1
  64. package/dist/commands/explain/explain.js +89 -13
  65. package/dist/commands/explain/explain.js.map +1 -1
  66. package/dist/commands/explore.js +16 -10
  67. package/dist/commands/explore.js.map +1 -1
  68. package/dist/commands/gate/gate.js +45 -8
  69. package/dist/commands/gate/gate.js.map +1 -1
  70. package/dist/commands/gate/print.js +15 -3
  71. package/dist/commands/gate/print.js.map +1 -1
  72. package/dist/commands/gherkin/commit.js +13 -54
  73. package/dist/commands/gherkin/commit.js.map +1 -1
  74. package/dist/commands/gherkin/gherkin.js +36 -20
  75. package/dist/commands/gherkin/gherkin.js.map +1 -1
  76. package/dist/commands/gherkin/reconcile.js +10 -13
  77. package/dist/commands/gherkin/reconcile.js.map +1 -1
  78. package/dist/commands/init/first-hour.js +44 -0
  79. package/dist/commands/init/first-hour.js.map +1 -1
  80. package/dist/commands/init/init.js +18 -22
  81. package/dist/commands/init/init.js.map +1 -1
  82. package/dist/commands/list/campaign/campaign.js +4 -6
  83. package/dist/commands/list/campaign/campaign.js.map +1 -1
  84. package/dist/commands/list/json.js +5 -7
  85. package/dist/commands/list/json.js.map +1 -1
  86. package/dist/commands/list/list.js +44 -29
  87. package/dist/commands/list/list.js.map +1 -1
  88. package/dist/commands/list/print.js +52 -9
  89. package/dist/commands/list/print.js.map +1 -1
  90. package/dist/commands/list/suites/suites.js +140 -0
  91. package/dist/commands/list/suites/suites.js.map +1 -0
  92. package/dist/commands/list/views.js +37 -34
  93. package/dist/commands/list/views.js.map +1 -1
  94. package/dist/commands/mcp/dispatch.js +91 -36
  95. package/dist/commands/mcp/dispatch.js.map +1 -1
  96. package/dist/commands/mcp/mcp.js +56 -15
  97. package/dist/commands/mcp/mcp.js.map +1 -1
  98. package/dist/commands/mcp/serve.js.map +1 -1
  99. package/dist/commands/migrate-openspec/materialize/active.js +27 -22
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
  101. package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
  102. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
  103. package/dist/commands/migrate-openspec/materialize/target.js +2 -2
  104. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
  105. package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
  106. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
  107. package/dist/commands/migrate-openspec/openspec/error.js +7 -8
  108. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
  109. package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
  110. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
  111. package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
  112. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
  113. package/dist/commands/new/capability-template.js +118 -0
  114. package/dist/commands/new/capability-template.js.map +1 -0
  115. package/dist/commands/new/commit.js +8 -22
  116. package/dist/commands/new/commit.js.map +1 -1
  117. package/dist/commands/new/modifies.js +206 -0
  118. package/dist/commands/new/modifies.js.map +1 -0
  119. package/dist/commands/new/new.js +59 -14
  120. package/dist/commands/new/new.js.map +1 -1
  121. package/dist/commands/new/templates.js +48 -124
  122. package/dist/commands/new/templates.js.map +1 -1
  123. package/dist/commands/open.js +3 -7
  124. package/dist/commands/open.js.map +1 -1
  125. package/dist/commands/policy/gate.js +27 -0
  126. package/dist/commands/policy/gate.js.map +1 -1
  127. package/dist/commands/rebase/plan.js +2 -2
  128. package/dist/commands/rebase/plan.js.map +1 -1
  129. package/dist/commands/rebase/rebase.js +60 -35
  130. package/dist/commands/rebase/rebase.js.map +1 -1
  131. package/dist/commands/seed/commit.js +55 -26
  132. package/dist/commands/seed/commit.js.map +1 -1
  133. package/dist/commands/seed/plan.js +158 -13
  134. package/dist/commands/seed/plan.js.map +1 -1
  135. package/dist/commands/seed/seed.js +13 -11
  136. package/dist/commands/seed/seed.js.map +1 -1
  137. package/dist/commands/show/feature.js +9 -5
  138. package/dist/commands/show/feature.js.map +1 -1
  139. package/dist/commands/show/introduced.js +157 -0
  140. package/dist/commands/show/introduced.js.map +1 -0
  141. package/dist/commands/show/marks.js +35 -6
  142. package/dist/commands/show/marks.js.map +1 -1
  143. package/dist/commands/show/service.js +41 -33
  144. package/dist/commands/show/service.js.map +1 -1
  145. package/dist/commands/show/show.js +17 -7
  146. package/dist/commands/show/show.js.map +1 -1
  147. package/dist/commands/status/fleet-next.js +90 -0
  148. package/dist/commands/status/fleet-next.js.map +1 -0
  149. package/dist/commands/status/json.js +2 -0
  150. package/dist/commands/status/json.js.map +1 -1
  151. package/dist/commands/status/print.js +89 -37
  152. package/dist/commands/status/print.js.map +1 -1
  153. package/dist/commands/status/status.js +3 -7
  154. package/dist/commands/status/status.js.map +1 -1
  155. package/dist/commands/steps/steps.js +103 -23
  156. package/dist/commands/steps/steps.js.map +1 -1
  157. package/dist/commands/subsystem/edit.js +7 -7
  158. package/dist/commands/subsystem/edit.js.map +1 -1
  159. package/dist/commands/subsystem/move.js +106 -46
  160. package/dist/commands/subsystem/move.js.map +1 -1
  161. package/dist/commands/subsystem/query.js +2 -4
  162. package/dist/commands/subsystem/query.js.map +1 -1
  163. package/dist/commands/subsystem/subsystem.js +4 -7
  164. package/dist/commands/subsystem/subsystem.js.map +1 -1
  165. package/dist/commands/subsystem/sync.js +289 -74
  166. package/dist/commands/subsystem/sync.js.map +1 -1
  167. package/dist/commands/subsystem/txn/exclude.js +132 -0
  168. package/dist/commands/subsystem/txn/exclude.js.map +1 -0
  169. package/dist/commands/subsystem/txn/links.js +128 -0
  170. package/dist/commands/subsystem/txn/links.js.map +1 -0
  171. package/dist/commands/subsystem/txn/report.js +174 -0
  172. package/dist/commands/subsystem/txn/report.js.map +1 -0
  173. package/dist/commands/subsystem/txn/txn.js +147 -45
  174. package/dist/commands/subsystem/txn/txn.js.map +1 -1
  175. package/dist/commands/subsystem/txn/views.js +200 -21
  176. package/dist/commands/subsystem/txn/views.js.map +1 -1
  177. package/dist/commands/tasks/actions.js +107 -0
  178. package/dist/commands/tasks/actions.js.map +1 -0
  179. package/dist/commands/tasks/render.js +253 -0
  180. package/dist/commands/tasks/render.js.map +1 -0
  181. package/dist/commands/tasks/tasks.js +119 -0
  182. package/dist/commands/tasks/tasks.js.map +1 -0
  183. package/dist/commands/unarchive/manifest.js +42 -26
  184. package/dist/commands/unarchive/manifest.js.map +1 -1
  185. package/dist/commands/unarchive/restore.js +83 -33
  186. package/dist/commands/unarchive/restore.js.map +1 -1
  187. package/dist/commands/unarchive/unarchive.js +22 -30
  188. package/dist/commands/unarchive/unarchive.js.map +1 -1
  189. package/dist/commands/validate/arch-coverage.js +78 -27
  190. package/dist/commands/validate/arch-coverage.js.map +1 -1
  191. package/dist/commands/validate/checks/fleet-shape.js +34 -126
  192. package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
  193. package/dist/commands/validate/checks/health-deps.js +9 -6
  194. package/dist/commands/validate/checks/health-deps.js.map +1 -1
  195. package/dist/commands/validate/checks/introduced.js +102 -0
  196. package/dist/commands/validate/checks/introduced.js.map +1 -0
  197. package/dist/commands/validate/checks/requirements.js +18 -8
  198. package/dist/commands/validate/checks/requirements.js.map +1 -1
  199. package/dist/commands/validate/checks/sources.js +11 -2
  200. package/dist/commands/validate/checks/sources.js.map +1 -1
  201. package/dist/commands/validate/coverage/covers-after.js +105 -0
  202. package/dist/commands/validate/coverage/covers-after.js.map +1 -0
  203. package/dist/commands/validate/feature.js +26 -20
  204. package/dist/commands/validate/feature.js.map +1 -1
  205. package/dist/commands/validate/fleet/census.js +37 -13
  206. package/dist/commands/validate/fleet/census.js.map +1 -1
  207. package/dist/commands/validate/fleet/landscape.js +144 -126
  208. package/dist/commands/validate/fleet/landscape.js.map +1 -1
  209. package/dist/commands/validate/fleet/load.js +66 -0
  210. package/dist/commands/validate/fleet/load.js.map +1 -1
  211. package/dist/commands/validate/fleet/map/attest.js +98 -0
  212. package/dist/commands/validate/fleet/map/attest.js.map +1 -0
  213. package/dist/commands/validate/fleet/map/bindings.js +107 -0
  214. package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
  215. package/dist/commands/validate/fleet/map/consumers.js +244 -0
  216. package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
  217. package/dist/commands/validate/fleet/map/isolation.js +116 -0
  218. package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
  219. package/dist/commands/validate/fleet/map/usecases.js +54 -0
  220. package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
  221. package/dist/commands/validate/fleet/obligations.js +44 -14
  222. package/dist/commands/validate/fleet/obligations.js.map +1 -1
  223. package/dist/commands/validate/fleet/scorecard/print.js +10 -2
  224. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
  225. package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
  226. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
  227. package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
  228. package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
  229. package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
  230. package/dist/commands/validate/fleet/views/ids.js +50 -15
  231. package/dist/commands/validate/fleet/views/ids.js.map +1 -1
  232. package/dist/commands/validate/fleet/views/projects.js +278 -0
  233. package/dist/commands/validate/fleet/views/projects.js.map +1 -0
  234. package/dist/commands/validate/fleet/views/root-documents.js +83 -0
  235. package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
  236. package/dist/commands/validate/fleet/views/stale.js +40 -47
  237. package/dist/commands/validate/fleet/views/stale.js.map +1 -1
  238. package/dist/commands/validate/links/corpus.js +1 -4
  239. package/dist/commands/validate/links/corpus.js.map +1 -1
  240. package/dist/commands/validate/links/glossary.js.map +1 -1
  241. package/dist/commands/validate/render/repeats.js +63 -0
  242. package/dist/commands/validate/render/repeats.js.map +1 -0
  243. package/dist/commands/validate/render/rollups.js +115 -0
  244. package/dist/commands/validate/render/rollups.js.map +1 -0
  245. package/dist/commands/validate/render/text.js +199 -0
  246. package/dist/commands/validate/render/text.js.map +1 -0
  247. package/dist/commands/validate/report.js +36 -160
  248. package/dist/commands/validate/report.js.map +1 -1
  249. package/dist/commands/validate/scope.js +113 -0
  250. package/dist/commands/validate/scope.js.map +1 -0
  251. package/dist/commands/validate/service/api.js +21 -107
  252. package/dist/commands/validate/service/api.js.map +1 -1
  253. package/dist/commands/validate/service/contracts/generated.js +134 -0
  254. package/dist/commands/validate/service/contracts/generated.js.map +1 -0
  255. package/dist/commands/validate/service/events/events.js +36 -14
  256. package/dist/commands/validate/service/events/events.js.map +1 -1
  257. package/dist/commands/validate/service/events/message-producers.js +1 -1
  258. package/dist/commands/validate/service/events/message-producers.js.map +1 -1
  259. package/dist/commands/validate/service/evidence-pins.js +56 -10
  260. package/dist/commands/validate/service/evidence-pins.js.map +1 -1
  261. package/dist/commands/validate/service/model/diverged.js +226 -0
  262. package/dist/commands/validate/service/model/diverged.js.map +1 -0
  263. package/dist/commands/validate/service/model/grade.js +163 -0
  264. package/dist/commands/validate/service/model/grade.js.map +1 -0
  265. package/dist/commands/validate/service/service.js +121 -61
  266. package/dist/commands/validate/service/service.js.map +1 -1
  267. package/dist/commands/validate/service/specs.js +88 -28
  268. package/dist/commands/validate/service/specs.js.map +1 -1
  269. package/dist/commands/validate/service/spine.js +38 -3
  270. package/dist/commands/validate/service/spine.js.map +1 -1
  271. package/dist/commands/validate/service/usecases/tags.js +113 -0
  272. package/dist/commands/validate/service/usecases/tags.js.map +1 -0
  273. package/dist/commands/validate/service/usecases/usecases.js +209 -0
  274. package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
  275. package/dist/commands/validate/validate.js +85 -60
  276. package/dist/commands/validate/validate.js.map +1 -1
  277. package/dist/commands/verify/cross/diff.js +2 -6
  278. package/dist/commands/verify/cross/diff.js.map +1 -1
  279. package/dist/commands/verify/evidence/contract.js +1 -1
  280. package/dist/commands/verify/evidence/contract.js.map +1 -1
  281. package/dist/commands/verify/evidence/read.js +12 -3
  282. package/dist/commands/verify/evidence/read.js.map +1 -1
  283. package/dist/commands/verify/frozen.js +45 -23
  284. package/dist/commands/verify/frozen.js.map +1 -1
  285. package/dist/commands/verify/record.js +69 -17
  286. package/dist/commands/verify/record.js.map +1 -1
  287. package/dist/commands/verify/report.js +27 -44
  288. package/dist/commands/verify/report.js.map +1 -1
  289. package/dist/commands/verify/results.js +112 -30
  290. package/dist/commands/verify/results.js.map +1 -1
  291. package/dist/commands/verify/verify.js +55 -52
  292. package/dist/commands/verify/verify.js.map +1 -1
  293. package/dist/commands/vouch/pack/pack.js +2 -2
  294. package/dist/commands/vouch/pack/pack.js.map +1 -1
  295. package/dist/commands/vouch/pack/print.js +1 -6
  296. package/dist/commands/vouch/pack/print.js.map +1 -1
  297. package/dist/commands/vouch/pack/sections.js +1 -2
  298. package/dist/commands/vouch/pack/sections.js.map +1 -1
  299. package/dist/commands/vouch/run.js +26 -81
  300. package/dist/commands/vouch/run.js.map +1 -1
  301. package/dist/commands/vouch/sample/plan.js +20 -34
  302. package/dist/commands/vouch/sample/plan.js.map +1 -1
  303. package/dist/commands/vouch/vet/verify.js +39 -7
  304. package/dist/commands/vouch/vet/verify.js.map +1 -1
  305. package/dist/commands/vouch/vouch.js +10 -17
  306. package/dist/commands/vouch/vouch.js.map +1 -1
  307. package/dist/core/agent/agents-md/command-map.js +81 -106
  308. package/dist/core/agent/agents-md/command-map.js.map +1 -1
  309. package/dist/core/agent/agents-md/map/explain.js +21 -9
  310. package/dist/core/agent/agents-md/map/explain.js.map +1 -1
  311. package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
  312. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
  313. package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
  314. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
  315. package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
  316. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
  317. package/dist/core/agent/agents-md/map/mcp.js +10 -7
  318. package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
  319. package/dist/core/agent/agents-md/map/status.js +24 -8
  320. package/dist/core/agent/agents-md/map/status.js.map +1 -1
  321. package/dist/core/agent/agents-md/map/subsystem.js +80 -8
  322. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
  323. package/dist/core/agent/agents-md/map/usecases.js +88 -10
  324. package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
  325. package/dist/core/agent/agents-md/refusals.js +49 -22
  326. package/dist/core/agent/agents-md/refusals.js.map +1 -1
  327. package/dist/core/agent/agents-md.js +39 -6
  328. package/dist/core/agent/agents-md.js.map +1 -1
  329. package/dist/core/agent/agents-stamp.js +10 -0
  330. package/dist/core/agent/agents-stamp.js.map +1 -1
  331. package/dist/core/agent/protocol.js +1 -1
  332. package/dist/core/agent/protocol.js.map +1 -1
  333. package/dist/core/agent/scaffold.js +10 -15
  334. package/dist/core/agent/scaffold.js.map +1 -1
  335. package/dist/core/agent/support/report.js +46 -5
  336. package/dist/core/agent/support/report.js.map +1 -1
  337. package/dist/core/agent/tools/dialects.js +1 -0
  338. package/dist/core/agent/tools/dialects.js.map +1 -1
  339. package/dist/core/agent/tools/registry.js +41 -76
  340. package/dist/core/agent/tools/registry.js.map +1 -1
  341. package/dist/core/agent/workflows/adopt.js +158 -11
  342. package/dist/core/agent/workflows/adopt.js.map +1 -1
  343. package/dist/core/agent/workflows/check.js +78 -39
  344. package/dist/core/agent/workflows/check.js.map +1 -1
  345. package/dist/core/agent/workflows/closing.js +88 -27
  346. package/dist/core/agent/workflows/closing.js.map +1 -1
  347. package/dist/core/agent/workflows/feature.js +128 -13
  348. package/dist/core/agent/workflows/feature.js.map +1 -1
  349. package/dist/core/agent/workflows/implement.js +96 -13
  350. package/dist/core/agent/workflows/implement.js.map +1 -1
  351. package/dist/core/agent/workflows/reference/authoring.js +106 -4
  352. package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
  353. package/dist/core/agent/workflows/reference/codes.js +8 -6
  354. package/dist/core/agent/workflows/reference/codes.js.map +1 -1
  355. package/dist/core/agent/workflows/reference/done-check.js +37 -15
  356. package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
  357. package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
  358. package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
  359. package/dist/core/agent/workflows/reference/reference.js +6 -3
  360. package/dist/core/agent/workflows/reference/reference.js.map +1 -1
  361. package/dist/core/agent/workflows/reference/spine.js +68 -27
  362. package/dist/core/agent/workflows/reference/spine.js.map +1 -1
  363. package/dist/core/asyncapi/baseline/plan.js +14 -81
  364. package/dist/core/asyncapi/baseline/plan.js.map +1 -1
  365. package/dist/core/asyncapi/digest.js +30 -25
  366. package/dist/core/asyncapi/digest.js.map +1 -1
  367. package/dist/core/asyncapi/merge/components.js +5 -4
  368. package/dist/core/asyncapi/merge/components.js.map +1 -1
  369. package/dist/core/asyncapi/merge/error.js +7 -2
  370. package/dist/core/asyncapi/merge/error.js.map +1 -1
  371. package/dist/core/asyncapi/merge/markers.js +32 -50
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -1
  373. package/dist/core/asyncapi/merge/merge.js +33 -53
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -1
  375. package/dist/core/asyncapi/merge/pin.js +12 -36
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -1
  377. package/dist/core/asyncapi/model.js.map +1 -1
  378. package/dist/core/asyncapi/read.js +36 -12
  379. package/dist/core/asyncapi/read.js.map +1 -1
  380. package/dist/core/brief/brief.js +96 -36
  381. package/dist/core/brief/brief.js.map +1 -1
  382. package/dist/core/brief/checks.js +138 -3
  383. package/dist/core/brief/checks.js.map +1 -1
  384. package/dist/core/brief/landscape.js +209 -130
  385. package/dist/core/brief/landscape.js.map +1 -1
  386. package/dist/core/brief/map/extend.js +84 -0
  387. package/dist/core/brief/map/extend.js.map +1 -0
  388. package/dist/core/brief/map/owed.js +266 -0
  389. package/dist/core/brief/map/owed.js.map +1 -0
  390. package/dist/core/brief/targets.js +109 -31
  391. package/dist/core/brief/targets.js.map +1 -1
  392. package/dist/core/brief/unchecked.js +14 -3
  393. package/dist/core/brief/unchecked.js.map +1 -1
  394. package/dist/core/c4/arch.js +90 -182
  395. package/dist/core/c4/arch.js.map +1 -1
  396. package/dist/core/c4/direction/direction.js +114 -0
  397. package/dist/core/c4/direction/direction.js.map +1 -0
  398. package/dist/core/c4/likec4.js +169 -14
  399. package/dist/core/c4/likec4.js.map +1 -1
  400. package/dist/core/c4/locate/unlocated.js +150 -0
  401. package/dist/core/c4/locate/unlocated.js.map +1 -0
  402. package/dist/core/c4/parsed/deployment.js +160 -0
  403. package/dist/core/c4/parsed/deployment.js.map +1 -0
  404. package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
  405. package/dist/core/c4/parsed/values.js +54 -1
  406. package/dist/core/c4/parsed/values.js.map +1 -1
  407. package/dist/core/c4/parsed/view-ids.js +39 -1
  408. package/dist/core/c4/parsed/view-ids.js.map +1 -1
  409. package/dist/core/c4/prefetch/fleet.js +134 -0
  410. package/dist/core/c4/prefetch/fleet.js.map +1 -0
  411. package/dist/core/c4/project/architecture.js +123 -7
  412. package/dist/core/c4/project/architecture.js.map +1 -1
  413. package/dist/core/c4/project/batch.js +140 -0
  414. package/dist/core/c4/project/batch.js.map +1 -0
  415. package/dist/core/c4/project/documents.js +43 -12
  416. package/dist/core/c4/project/documents.js.map +1 -1
  417. package/dist/core/c4/project/load.js +79 -43
  418. package/dist/core/c4/project/load.js.map +1 -1
  419. package/dist/core/c4/project/staged.js +277 -0
  420. package/dist/core/c4/project/staged.js.map +1 -0
  421. package/dist/core/c4/resolve/attested.js +0 -0
  422. package/dist/core/c4/resolve/attested.js.map +1 -0
  423. package/dist/core/c4/resolve/external.js +51 -0
  424. package/dist/core/c4/resolve/external.js.map +1 -0
  425. package/dist/core/c4/resolve/service.js +78 -17
  426. package/dist/core/c4/resolve/service.js.map +1 -1
  427. package/dist/core/c4/resolve/steps.js +290 -0
  428. package/dist/core/c4/resolve/steps.js.map +1 -0
  429. package/dist/core/c4/resolve/systems.js +154 -0
  430. package/dist/core/c4/resolve/systems.js.map +1 -0
  431. package/dist/core/c4/root-project/exclude.js +352 -0
  432. package/dist/core/c4/root-project/exclude.js.map +1 -0
  433. package/dist/core/c4/seed/template.js +65 -1
  434. package/dist/core/c4/seed/template.js.map +1 -1
  435. package/dist/core/c4/service-model/created/models.js +96 -0
  436. package/dist/core/c4/service-model/created/models.js.map +1 -0
  437. package/dist/core/c4/service-model/documents.js +47 -0
  438. package/dist/core/c4/service-model/documents.js.map +1 -0
  439. package/dist/core/c4/service-model/fleet/extending.js +82 -0
  440. package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
  441. package/dist/core/c4/service-model/load.js +165 -0
  442. package/dist/core/c4/service-model/load.js.map +1 -0
  443. package/dist/core/c4/service-model/renderer.js +128 -0
  444. package/dist/core/c4/service-model/renderer.js.map +1 -0
  445. package/dist/core/c4/service-model/shape.js +106 -0
  446. package/dist/core/c4/service-model/shape.js.map +1 -0
  447. package/dist/core/c4/service-model/slice.js +188 -0
  448. package/dist/core/c4/service-model/slice.js.map +1 -0
  449. package/dist/core/c4/source-mask.js +297 -10
  450. package/dist/core/c4/source-mask.js.map +1 -1
  451. package/dist/core/c4/source-scan.js +222 -141
  452. package/dist/core/c4/source-scan.js.map +1 -1
  453. package/dist/core/c4/splice/authored-source.js +79 -13
  454. package/dist/core/c4/splice/authored-source.js.map +1 -1
  455. package/dist/core/c4/splice/contract.js +20 -0
  456. package/dist/core/c4/splice/contract.js.map +1 -1
  457. package/dist/core/c4/splice/delta-blocks.js +20 -20
  458. package/dist/core/c4/splice/delta-blocks.js.map +1 -1
  459. package/dist/core/c4/splice/identity/declared.js +87 -0
  460. package/dist/core/c4/splice/identity/declared.js.map +1 -0
  461. package/dist/core/c4/splice/identity/edges.js +37 -0
  462. package/dist/core/c4/splice/identity/edges.js.map +1 -1
  463. package/dist/core/c4/splice/identity/existing.js +80 -0
  464. package/dist/core/c4/splice/identity/existing.js.map +1 -0
  465. package/dist/core/c4/splice/identity/interior.js +80 -0
  466. package/dist/core/c4/splice/identity/interior.js.map +1 -0
  467. package/dist/core/c4/splice/identity/owner.js +50 -0
  468. package/dist/core/c4/splice/identity/owner.js.map +1 -0
  469. package/dist/core/c4/splice/landscape-merge.js +182 -114
  470. package/dist/core/c4/splice/landscape-merge.js.map +1 -1
  471. package/dist/core/c4/splice/model/merge.js +151 -0
  472. package/dist/core/c4/splice/model/merge.js.map +1 -0
  473. package/dist/core/c4/splice/placement.js +160 -56
  474. package/dist/core/c4/splice/placement.js.map +1 -1
  475. package/dist/core/c4/splice/text/spot.js +131 -0
  476. package/dist/core/c4/splice/text/spot.js.map +1 -0
  477. package/dist/core/c4/workspace.js +13 -19
  478. package/dist/core/c4/workspace.js.map +1 -1
  479. package/dist/core/capabilities/capabilities.js +6 -6
  480. package/dist/core/capabilities/capabilities.js.map +1 -1
  481. package/dist/core/capabilities/delta/doc.js +7 -10
  482. package/dist/core/capabilities/delta/doc.js.map +1 -1
  483. package/dist/core/capabilities/delta/uncovered.js +2 -2
  484. package/dist/core/capabilities/delta/uncovered.js.map +1 -1
  485. package/dist/core/capabilities/findings.js +18 -22
  486. package/dist/core/capabilities/findings.js.map +1 -1
  487. package/dist/core/capabilities/realizes/findings.js +12 -19
  488. package/dist/core/capabilities/realizes/findings.js.map +1 -1
  489. package/dist/core/capabilities/realizes/join.js +0 -20
  490. package/dist/core/capabilities/realizes/join.js.map +1 -1
  491. package/dist/core/capabilities/rollup.js +6 -0
  492. package/dist/core/capabilities/rollup.js.map +1 -1
  493. package/dist/core/capabilities/tree.js +6 -28
  494. package/dist/core/capabilities/tree.js.map +1 -1
  495. package/dist/core/capabilities/usecase-join.js +3 -13
  496. package/dist/core/capabilities/usecase-join.js.map +1 -1
  497. package/dist/core/coherence/authoring/scaffold.js +11 -3
  498. package/dist/core/coherence/authoring/scaffold.js.map +1 -1
  499. package/dist/core/coherence/authoring/sentinels.js +2 -1
  500. package/dist/core/coherence/authoring/sentinels.js.map +1 -1
  501. package/dist/core/coherence/calls/called.js +52 -0
  502. package/dist/core/coherence/calls/called.js.map +1 -0
  503. package/dist/core/coherence/coherence.js +103 -22
  504. package/dist/core/coherence/coherence.js.map +1 -1
  505. package/dist/core/coherence/consumed/operations.js +70 -0
  506. package/dist/core/coherence/consumed/operations.js.map +1 -0
  507. package/dist/core/coherence/consumed/refusal.js +38 -0
  508. package/dist/core/coherence/consumed/refusal.js.map +1 -0
  509. package/dist/core/coherence/covers/health.js +85 -0
  510. package/dist/core/coherence/covers/health.js.map +1 -0
  511. package/dist/core/coherence/declared.js +60 -34
  512. package/dist/core/coherence/declared.js.map +1 -1
  513. package/dist/core/coherence/events/declared.js +44 -59
  514. package/dist/core/coherence/events/declared.js.map +1 -1
  515. package/dist/core/coherence/events/events.js +147 -16
  516. package/dist/core/coherence/events/events.js.map +1 -1
  517. package/dist/core/coherence/events/grades.js +31 -8
  518. package/dist/core/coherence/events/grades.js.map +1 -1
  519. package/dist/core/coherence/events/lookups.js +28 -87
  520. package/dist/core/coherence/events/lookups.js.map +1 -1
  521. package/dist/core/coherence/events/produced.js +100 -0
  522. package/dist/core/coherence/events/produced.js.map +1 -0
  523. package/dist/core/coherence/lookups.js +18 -78
  524. package/dist/core/coherence/lookups.js.map +1 -1
  525. package/dist/core/coherence/retired/archived.js +285 -0
  526. package/dist/core/coherence/retired/archived.js.map +1 -0
  527. package/dist/core/coherence/retired/event-pair.js +62 -0
  528. package/dist/core/coherence/retired/event-pair.js.map +1 -0
  529. package/dist/core/coherence/retired/merged.js +79 -0
  530. package/dist/core/coherence/retired/merged.js.map +1 -0
  531. package/dist/core/coherence/retired/retirement.js +49 -0
  532. package/dist/core/coherence/retired/retirement.js.map +1 -0
  533. package/dist/core/coherence/retired/revival/arrivals.js +131 -0
  534. package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
  535. package/dist/core/coherence/retired/revival/revived.js +297 -0
  536. package/dist/core/coherence/retired/revival/revived.js.map +1 -0
  537. package/dist/core/consumers/after.js +113 -0
  538. package/dist/core/consumers/after.js.map +1 -0
  539. package/dist/core/consumers/consumers.js +296 -0
  540. package/dist/core/consumers/consumers.js.map +1 -0
  541. package/dist/core/consumers/current.js +211 -0
  542. package/dist/core/consumers/current.js.map +1 -0
  543. package/dist/core/consumers/scan.js +46 -0
  544. package/dist/core/consumers/scan.js.map +1 -0
  545. package/dist/core/consumers/state.js +27 -0
  546. package/dist/core/consumers/state.js.map +1 -0
  547. package/dist/core/delta/delta.js +51 -6
  548. package/dist/core/delta/delta.js.map +1 -1
  549. package/dist/core/dependencies/fanin.js +2 -2
  550. package/dist/core/dependencies/fanin.js.map +1 -1
  551. package/dist/core/deployment/delta.js +191 -0
  552. package/dist/core/deployment/delta.js.map +1 -0
  553. package/dist/core/deployment/objects.js +145 -0
  554. package/dist/core/deployment/objects.js.map +1 -0
  555. package/dist/core/diff/base-git.js +94 -12
  556. package/dist/core/diff/base-git.js.map +1 -1
  557. package/dist/core/diff/base-state.js +49 -21
  558. package/dist/core/diff/base-state.js.map +1 -1
  559. package/dist/core/diff/joins.js +136 -0
  560. package/dist/core/diff/joins.js.map +1 -0
  561. package/dist/core/diff/scope/changed-paths.js +22 -6
  562. package/dist/core/diff/scope/changed-paths.js.map +1 -1
  563. package/dist/core/diff/scope/edges.js +82 -0
  564. package/dist/core/diff/scope/edges.js.map +1 -0
  565. package/dist/core/diff/semantic.js +100 -31
  566. package/dist/core/diff/semantic.js.map +1 -1
  567. package/dist/core/docs.js +31 -22
  568. package/dist/core/docs.js.map +1 -1
  569. package/dist/core/doctor/agents.js +34 -18
  570. package/dist/core/doctor/agents.js.map +1 -1
  571. package/dist/core/doctor/config.js +7 -3
  572. package/dist/core/doctor/config.js.map +1 -1
  573. package/dist/core/doctor/doctor.js +91 -11
  574. package/dist/core/doctor/doctor.js.map +1 -1
  575. package/dist/core/doctor/report.js.map +1 -1
  576. package/dist/core/doctor/reports/scan.js +242 -0
  577. package/dist/core/doctor/reports/scan.js.map +1 -0
  578. package/dist/core/document/apply.js +158 -11
  579. package/dist/core/document/apply.js.map +1 -1
  580. package/dist/core/document/frontmatter.js +55 -0
  581. package/dist/core/document/frontmatter.js.map +1 -1
  582. package/dist/core/document/parse.js +29 -6
  583. package/dist/core/document/parse.js.map +1 -1
  584. package/dist/core/document/scenarios.js +17 -0
  585. package/dist/core/document/scenarios.js.map +1 -1
  586. package/dist/core/document/spec.js +4 -11
  587. package/dist/core/document/spec.js.map +1 -1
  588. package/dist/core/envelope/config.js +10 -0
  589. package/dist/core/envelope/config.js.map +1 -1
  590. package/dist/core/envelope/json.js +34 -0
  591. package/dist/core/envelope/json.js.map +1 -1
  592. package/dist/core/explain/families.js +26 -21
  593. package/dist/core/explain/families.js.map +1 -1
  594. package/dist/core/explain/lookup.js +14 -1
  595. package/dist/core/explain/lookup.js.map +1 -1
  596. package/dist/core/explain/refusals.js +5 -5
  597. package/dist/core/explain/refusals.js.map +1 -1
  598. package/dist/core/explain/terms/documents.js +227 -0
  599. package/dist/core/explain/terms/documents.js.map +1 -0
  600. package/dist/core/explain/terms/done-check.js +144 -0
  601. package/dist/core/explain/terms/done-check.js.map +1 -0
  602. package/dist/core/explain/terms/entry.js +14 -0
  603. package/dist/core/explain/terms/entry.js.map +1 -0
  604. package/dist/core/explain/terms/stages.js +71 -0
  605. package/dist/core/explain/terms/stages.js.map +1 -0
  606. package/dist/core/explain/terms/tasks.js +97 -0
  607. package/dist/core/explain/terms/tasks.js.map +1 -0
  608. package/dist/core/explain/terms.js +68 -109
  609. package/dist/core/explain/terms.js.map +1 -1
  610. package/dist/core/explore/explore.js +40 -10
  611. package/dist/core/explore/explore.js.map +1 -1
  612. package/dist/core/fleet-context.js +81 -95
  613. package/dist/core/fleet-context.js.map +1 -1
  614. package/dist/core/gate/checks.js +11 -4
  615. package/dist/core/gate/checks.js.map +1 -1
  616. package/dist/core/gate/partners.js +69 -28
  617. package/dist/core/gate/partners.js.map +1 -1
  618. package/dist/core/gate/report.js.map +1 -1
  619. package/dist/core/gate/verification.js +75 -12
  620. package/dist/core/gate/verification.js.map +1 -1
  621. package/dist/core/gherkin/stale.js +13 -37
  622. package/dist/core/gherkin/stale.js.map +1 -1
  623. package/dist/core/glossary/delta.js +10 -7
  624. package/dist/core/glossary/delta.js.map +1 -1
  625. package/dist/core/glossary/tree.js +6 -19
  626. package/dist/core/glossary/tree.js.map +1 -1
  627. package/dist/core/kernel/concurrency.js +19 -0
  628. package/dist/core/kernel/concurrency.js.map +1 -1
  629. package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
  630. package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
  631. package/dist/core/kernel/path-safety.js +38 -0
  632. package/dist/core/kernel/path-safety.js.map +1 -1
  633. package/dist/core/kernel/records.js +11 -0
  634. package/dist/core/kernel/records.js.map +1 -1
  635. package/dist/core/links/backlinks.js +1 -3
  636. package/dist/core/links/backlinks.js.map +1 -1
  637. package/dist/core/links/corpus.js +51 -37
  638. package/dist/core/links/corpus.js.map +1 -1
  639. package/dist/core/links/findings.js.map +1 -1
  640. package/dist/core/links/parse.js +38 -9
  641. package/dist/core/links/parse.js.map +1 -1
  642. package/dist/core/mcp/argv.js +101 -0
  643. package/dist/core/mcp/argv.js.map +1 -1
  644. package/dist/core/mcp/author-tools.js +2 -1
  645. package/dist/core/mcp/author-tools.js.map +1 -1
  646. package/dist/core/mcp/protocol.js +2 -2
  647. package/dist/core/mcp/protocol.js.map +1 -1
  648. package/dist/core/mcp/tools.js +43 -33
  649. package/dist/core/mcp/tools.js.map +1 -1
  650. package/dist/core/openapi/baseline/gate.js +53 -12
  651. package/dist/core/openapi/baseline/gate.js.map +1 -1
  652. package/dist/core/openapi/baseline/plan.js +40 -10
  653. package/dist/core/openapi/baseline/plan.js.map +1 -1
  654. package/dist/core/openapi/baseline/record.js +0 -1
  655. package/dist/core/openapi/baseline/record.js.map +1 -1
  656. package/dist/core/openapi/contract-text.js +33 -0
  657. package/dist/core/openapi/contract-text.js.map +1 -0
  658. package/dist/core/openapi/contract-tree.js +301 -0
  659. package/dist/core/openapi/contract-tree.js.map +1 -0
  660. package/dist/core/openapi/depth.js +38 -24
  661. package/dist/core/openapi/depth.js.map +1 -1
  662. package/dist/core/openapi/digest.js +52 -2
  663. package/dist/core/openapi/digest.js.map +1 -1
  664. package/dist/core/openapi/doc.js +12 -4
  665. package/dist/core/openapi/doc.js.map +1 -1
  666. package/dist/core/openapi/merge/components.js +25 -60
  667. package/dist/core/openapi/merge/components.js.map +1 -1
  668. package/dist/core/openapi/merge/error.js +3 -13
  669. package/dist/core/openapi/merge/error.js.map +1 -1
  670. package/dist/core/openapi/merge/markers.js +26 -30
  671. package/dist/core/openapi/merge/markers.js.map +1 -1
  672. package/dist/core/openapi/merge/merge.js +28 -52
  673. package/dist/core/openapi/merge/merge.js.map +1 -1
  674. package/dist/core/openapi/merge/pin.js +17 -35
  675. package/dist/core/openapi/merge/pin.js.map +1 -1
  676. package/dist/core/openspec/decide/verdict.js.map +1 -1
  677. package/dist/core/openspec/inventory.js +4 -5
  678. package/dist/core/openspec/inventory.js.map +1 -1
  679. package/dist/core/openspec/model/mapping.js +30 -28
  680. package/dist/core/openspec/model/mapping.js.map +1 -1
  681. package/dist/core/openspec/model/model.js +12 -7
  682. package/dist/core/openspec/model/model.js.map +1 -1
  683. package/dist/core/openspec/scan/changes.js +10 -4
  684. package/dist/core/openspec/scan/changes.js.map +1 -1
  685. package/dist/core/openspec/scan/config.js +7 -16
  686. package/dist/core/openspec/scan/config.js.map +1 -1
  687. package/dist/core/openspec/scan/shape.js +63 -19
  688. package/dist/core/openspec/scan/shape.js.map +1 -1
  689. package/dist/core/openspec/scan/walk.js +21 -38
  690. package/dist/core/openspec/scan/walk.js.map +1 -1
  691. package/dist/core/pack/living.js +43 -4
  692. package/dist/core/pack/living.js.map +1 -1
  693. package/dist/core/pack/pack.js +50 -6
  694. package/dist/core/pack/pack.js.map +1 -1
  695. package/dist/core/projection/arch-slice.js +2 -2
  696. package/dist/core/projection/arch-slice.js.map +1 -1
  697. package/dist/core/projection/introduced.js +14 -0
  698. package/dist/core/projection/introduced.js.map +1 -0
  699. package/dist/core/provenance/findings.js +48 -12
  700. package/dist/core/provenance/findings.js.map +1 -1
  701. package/dist/core/provenance/git.js +33 -57
  702. package/dist/core/provenance/git.js.map +1 -1
  703. package/dist/core/provenance/gitq/moves.js +71 -114
  704. package/dist/core/provenance/gitq/moves.js.map +1 -1
  705. package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
  706. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
  707. package/dist/core/provenance/sources.js +23 -6
  708. package/dist/core/provenance/sources.js.map +1 -1
  709. package/dist/core/provenance/stamp.js +5 -17
  710. package/dist/core/provenance/stamp.js.map +1 -1
  711. package/dist/core/repo/authored/paths.js +18 -0
  712. package/dist/core/repo/authored/paths.js.map +1 -1
  713. package/dist/core/repo/entries.js.map +1 -1
  714. package/dist/core/repo/paths.js +92 -6
  715. package/dist/core/repo/paths.js.map +1 -1
  716. package/dist/core/repo/repo.js +74 -14
  717. package/dist/core/repo/repo.js.map +1 -1
  718. package/dist/core/repo/tree/find.js +12 -3
  719. package/dist/core/repo/tree/find.js.map +1 -1
  720. package/dist/core/repo/tree/fs.js +27 -1
  721. package/dist/core/repo/tree/fs.js.map +1 -1
  722. package/dist/core/repo/tree/render/body.js +258 -0
  723. package/dist/core/repo/tree/render/body.js.map +1 -0
  724. package/dist/core/repo/tree/render/projects.js +182 -0
  725. package/dist/core/repo/tree/render/projects.js.map +1 -0
  726. package/dist/core/repo/tree/render/service/header.js +125 -0
  727. package/dist/core/repo/tree/render/service/header.js.map +1 -0
  728. package/dist/core/repo/tree/render/service/peers.js +28 -0
  729. package/dist/core/repo/tree/render/service/peers.js.map +1 -0
  730. package/dist/core/repo/tree/render/service/view-id.js +67 -0
  731. package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
  732. package/dist/core/repo/tree/render/service/views.js +78 -0
  733. package/dist/core/repo/tree/render/service/views.js.map +1 -0
  734. package/dist/core/repo/tree/render/stale.js +42 -0
  735. package/dist/core/repo/tree/render/stale.js.map +1 -0
  736. package/dist/core/repo/tree/render/text.js +84 -0
  737. package/dist/core/repo/tree/render/text.js.map +1 -0
  738. package/dist/core/repo/tree/render/views.js +293 -0
  739. package/dist/core/repo/tree/render/views.js.map +1 -0
  740. package/dist/core/repo/tree/walk.js +53 -32
  741. package/dist/core/repo/tree/walk.js.map +1 -1
  742. package/dist/core/results.js +47 -12
  743. package/dist/core/results.js.map +1 -1
  744. package/dist/core/scaffold/landscape.js +121 -13
  745. package/dist/core/scaffold/landscape.js.map +1 -1
  746. package/dist/core/scaffold/legacy.js +271 -0
  747. package/dist/core/scaffold/legacy.js.map +1 -0
  748. package/dist/core/staging/commit.js +26 -15
  749. package/dist/core/staging/commit.js.map +1 -1
  750. package/dist/core/staging/lock.js +74 -12
  751. package/dist/core/staging/lock.js.map +1 -1
  752. package/dist/core/staging/recovery/intent.js +2 -5
  753. package/dist/core/staging/recovery/intent.js.map +1 -1
  754. package/dist/core/staging/recovery/recover.js +84 -6
  755. package/dist/core/staging/recovery/recover.js.map +1 -1
  756. package/dist/core/staging/snapshot/manifest.js +188 -0
  757. package/dist/core/staging/snapshot/manifest.js.map +1 -0
  758. package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
  759. package/dist/core/staging/snapshot/snapshot.js.map +1 -0
  760. package/dist/core/staging/txn/forward.js +24 -14
  761. package/dist/core/staging/txn/forward.js.map +1 -1
  762. package/dist/core/staging/writes.js.map +1 -1
  763. package/dist/core/status/actions/execution.js +11 -0
  764. package/dist/core/status/actions/execution.js.map +1 -1
  765. package/dist/core/status/contracts.js +39 -15
  766. package/dist/core/status/contracts.js.map +1 -1
  767. package/dist/core/status/feature/artifacts.js +33 -8
  768. package/dist/core/status/feature/artifacts.js.map +1 -1
  769. package/dist/core/status/feature/feature.js +24 -17
  770. package/dist/core/status/feature/feature.js.map +1 -1
  771. package/dist/core/status/feature/next.js +20 -7
  772. package/dist/core/status/feature/next.js.map +1 -1
  773. package/dist/core/status/feature/words.js +96 -0
  774. package/dist/core/status/feature/words.js.map +1 -0
  775. package/dist/core/status/fleet/fleet.js +43 -14
  776. package/dist/core/status/fleet/fleet.js.map +1 -1
  777. package/dist/core/status/fleet/next.js +153 -58
  778. package/dist/core/status/fleet/next.js.map +1 -1
  779. package/dist/core/status/report.js.map +1 -1
  780. package/dist/core/status/scan.js.map +1 -1
  781. package/dist/core/status/verification.js +30 -15
  782. package/dist/core/status/verification.js.map +1 -1
  783. package/dist/core/tasks/packet.js +199 -0
  784. package/dist/core/tasks/packet.js.map +1 -0
  785. package/dist/core/tasks/record.js +43 -0
  786. package/dist/core/tasks/record.js.map +1 -0
  787. package/dist/core/tasks/sources.js +181 -0
  788. package/dist/core/tasks/sources.js.map +1 -0
  789. package/dist/core/tasks/stamps.js +76 -0
  790. package/dist/core/tasks/stamps.js.map +1 -0
  791. package/dist/core/tasks/unit.js +0 -0
  792. package/dist/core/tasks/unit.js.map +1 -0
  793. package/dist/core/usecases/capability.js +11 -14
  794. package/dist/core/usecases/capability.js.map +1 -1
  795. package/dist/core/usecases/delta/claims.js +31 -7
  796. package/dist/core/usecases/delta/claims.js.map +1 -1
  797. package/dist/core/usecases/delta/flows.js +5 -7
  798. package/dist/core/usecases/delta/flows.js.map +1 -1
  799. package/dist/core/usecases/delta/modified.js +315 -0
  800. package/dist/core/usecases/delta/modified.js.map +1 -0
  801. package/dist/core/usecases/delta/overlay.js +32 -191
  802. package/dist/core/usecases/delta/overlay.js.map +1 -1
  803. package/dist/core/usecases/fleet.js +195 -36
  804. package/dist/core/usecases/fleet.js.map +1 -1
  805. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
  806. package/dist/core/usecases/grade/capability-tag.js.map +1 -0
  807. package/dist/core/usecases/grade/feature.js +88 -0
  808. package/dist/core/usecases/grade/feature.js.map +1 -0
  809. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
  810. package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
  811. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
  812. package/dist/core/usecases/grade/steps.js.map +1 -0
  813. package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
  814. package/dist/core/usecases/grade/usecases.js.map +1 -0
  815. package/dist/core/usecases/operations.js +26 -14
  816. package/dist/core/usecases/operations.js.map +1 -1
  817. package/dist/core/usecases/place.js.map +1 -1
  818. package/dist/core/usecases/service/exclude.js +119 -0
  819. package/dist/core/usecases/service/exclude.js.map +1 -0
  820. package/dist/core/usecases/service/flows.js +164 -0
  821. package/dist/core/usecases/service/flows.js.map +1 -0
  822. package/dist/core/usecases/suites/participants.js +83 -0
  823. package/dist/core/usecases/suites/participants.js.map +1 -0
  824. package/dist/core/usecases/suites/suites.js +45 -0
  825. package/dist/core/usecases/suites/suites.js.map +1 -0
  826. package/dist/core/verify/answers.js +12 -5
  827. package/dist/core/verify/answers.js.map +1 -1
  828. package/dist/core/verify/build.js +102 -82
  829. package/dist/core/verify/build.js.map +1 -1
  830. package/dist/core/verify/checklist.js +70 -12
  831. package/dist/core/verify/checklist.js.map +1 -1
  832. package/dist/core/verify/location.js +67 -0
  833. package/dist/core/verify/location.js.map +1 -0
  834. package/dist/core/verify/pins/lint.js +3 -2
  835. package/dist/core/verify/pins/lint.js.map +1 -1
  836. package/dist/core/verify/pins/pin.js +1 -1
  837. package/dist/core/verify/pins/pin.js.map +1 -1
  838. package/dist/core/verify/read/file.js +224 -0
  839. package/dist/core/verify/read/file.js.map +1 -0
  840. package/dist/core/verify/{file.js → read/shape.js} +16 -104
  841. package/dist/core/verify/read/shape.js.map +1 -0
  842. package/dist/core/verify/read/union.js +105 -0
  843. package/dist/core/verify/read/union.js.map +1 -0
  844. package/dist/core/verify/record.js +35 -12
  845. package/dist/core/verify/record.js.map +1 -1
  846. package/dist/core/verify/store/commit.js +40 -30
  847. package/dist/core/verify/store/commit.js.map +1 -1
  848. package/dist/core/verify/store/render.js +8 -4
  849. package/dist/core/verify/store/render.js.map +1 -1
  850. package/dist/core/vocabulary/issue.js +4 -0
  851. package/dist/core/vocabulary/issue.js.map +1 -1
  852. package/dist/core/vocabulary/maturity.js +75 -4
  853. package/dist/core/vocabulary/maturity.js.map +1 -1
  854. package/dist/core/vocabulary/report.js +16 -1
  855. package/dist/core/vocabulary/report.js.map +1 -1
  856. package/examples/README.md +54 -11
  857. package/examples/docs/architecture/landscape.likec4 +62 -6
  858. package/examples/docs/architecture/services.likec4 +70 -0
  859. package/examples/docs/architecture/subsystems.likec4 +7 -0
  860. package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
  861. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
  862. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
  863. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
  864. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
  865. package/examples/docs/likec4.config.json +0 -1
  866. package/examples/docs/services/checkout-web/model.likec4 +39 -56
  867. package/examples/docs/services/order-service/arch.spec.md +13 -6
  868. package/examples/docs/services/order-service/asyncapi.yaml +3 -2
  869. package/examples/docs/services/order-service/model.likec4 +58 -70
  870. package/examples/docs/services/payment-service/arch.spec.md +7 -4
  871. package/examples/docs/services/payment-service/model.likec4 +38 -37
  872. package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
  873. package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
  874. package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
  875. package/examples/docs/services/platform/notification-service/health.yaml +8 -3
  876. package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
  877. package/package.json +1 -1
  878. package/dist/commands/validate/checks/vocabulary.js +0 -31
  879. package/dist/commands/validate/checks/vocabulary.js.map +0 -1
  880. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
  881. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
  882. package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
  883. package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
  884. package/dist/core/agent/agents-md/artifacts.js +0 -220
  885. package/dist/core/agent/agents-md/artifacts.js.map +0 -1
  886. package/dist/core/agent/agents-md/cycle.js +0 -165
  887. package/dist/core/agent/agents-md/cycle.js.map +0 -1
  888. package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
  889. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
  890. package/dist/core/agent/agents-md/spine.js +0 -40
  891. package/dist/core/agent/agents-md/spine.js.map +0 -1
  892. package/dist/core/coherence/events/merged.js +0 -40
  893. package/dist/core/coherence/events/merged.js.map +0 -1
  894. package/dist/core/diff/current-state.js +0 -96
  895. package/dist/core/diff/current-state.js.map +0 -1
  896. package/dist/core/diff/victims.js +0 -247
  897. package/dist/core/diff/victims.js.map +0 -1
  898. package/dist/core/repo/tree/views.js +0 -172
  899. package/dist/core/repo/tree/views.js.map +0 -1
  900. package/dist/core/staging/snapshot.js.map +0 -1
  901. package/dist/core/verify/file.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diverged.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/model/diverged.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `c4.declaration-diverged` — the check the \"every shared element is declared\n * once per document\" report asked for.\n *\n * A STANDALONE model has to re-declare every element it talks about: it is\n * parsed alone, so `kafka`, `uaa` and each peer service appear a second time,\n * with a second kind, a second title, a second tag list and a second binding.\n * Nothing compared the copies. Measured on the fleet that filed the report: 78\n * double declarations across 56 services, and the copies had already drifted —\n * `#platform` was on the map's elements and missing from the models'.\n *\n * WHAT IS COMPARED, and why exactly these four. `kind`, `title`, the tag SET and\n * the `metadata { service }` binding each change what loam concludes or what a\n * view shows: the kind decides which specification rule applies and which\n * exemptions (`ACTOR_KINDS`, `#external`) fire, the title is a resolver fallback\n * when nothing is bound, a tag is graded (`#external`, `#obl-…`), and the\n * binding decides which directory an edge is filed under. `description` is\n * deliberately NOT compared: it is prose, nothing joins on it, and reporting two\n * differently-worded sentences as a divergence would train a reader to ignore\n * the code.\n *\n * WHICH TWO DECLARATIONS ARE ONE ELEMENT is `counterpart` below, and it is the\n * half the first release got wrong: it joined on the literal id, so in a GROUPED\n * map — one that nests its services under `marketplace`, as loam's own example\n * does — a model's `paymentService` never met the map's\n * `marketplace.paymentService` and the check was silent on every fleet that\n * groups (verification 2026-09-04, W3). The three rungs are ordered by how much\n * they assume: the id, then the `metadata { service }` binding both sides wrote,\n * then a unique dotted TAIL of the map's id. Ambiguity is never resolved by\n * picking one — a key two map elements answer to joins nothing, because a\n * divergence reported against the wrong peer is worse than the silence. And the\n * BARE last segment is a tail only when the map element it names is a\n * FLEET-LEVEL box: see `joinIndex`, where the first release of the tail rung\n * joined one service's `db` to another service's.\n *\n * WHAT \"FLEET-LEVEL\" MEANS, spelled once here because three pages spelled it\n * differently and all three were wrong (verification 2026-09-04, second pass):\n * a map element NO ANCESTOR of which stands for a service — `serviceLevelElements`,\n * the one predicate every fleet check asks this question with. That is a\n * top-level element, and equally a child of a plain grouping element: a map\n * that draws `infra = group 'Infra' { cache = database … }` is drawing the\n * fleet's cache, at fleet level, and a standalone model that declares its own\n * top-level `cache` IS shadowing that box — the two documents give one name two\n * meanings, which is the thing this code exists to report. What the rung must\n * never reach is an element drawn INSIDE a service's own element: that is\n * somebody's interior, it belongs to that service, and a model's private `db`\n * being told to copy another service's `db` verbatim is the wrong-join class\n * the tail rung was restricted for in the first place.\n *\n * SILENT WHEN THE MAP DOES NOT PARSE, and when the model does not: a diff\n * against half a document is invention, and both states already have a finding\n * of their own.\n *\n * The repair has two forms on purpose. Copying the map's declaration verbatim\n * keeps the standalone shape, which is legal forever; migrating the model to\n * extend the map removes the copy altogether, which is the only fix that cannot\n * drift again. Except where the first repair is a no-op, and `copyIsNoOp`\n * decides that by SIMULATING it rather than by pattern-matching one shape of it:\n * a tag difference that survives the copy comes off the two documents'\n * `specification` blocks, so the message names the block instead.\n */\nimport type { Elem, LoadedDoc } from \"../../../../core/c4/likec4.js\";\nimport type { DocSpecification } from \"../../../../core/c4/parsed/specification.js\";\nimport type { ServiceModel } from \"../../../../core/c4/service-model/load.js\";\nimport type { PathableService } from \"../../../../core/kernel/ids/service.js\";\nimport { ARTIFACT_FILES } from \"../../../../core/repo/paths.js\";\nimport type { Finding } from \"../../../../core/vocabulary/report.js\";\nimport { serviceLevelElements } from \"../../fleet/census.js\";\n\n/**\n * How the OTHER declaration is named.\n *\n * The document, not the file: `Elem` carries no source path, and the map is a\n * PROJECT — `architecture/landscape.likec4` plus every other `.likec4` under\n * `architecture/` — so naming the landscape file was a guess that sent an author\n * to a document that does not declare the element at all.\n */\nconst MAP = \"the fleet map (architecture/)\";\n\nexport interface DivergenceCheck {\n service: PathableService;\n model: ServiceModel;\n /** The `architecture/` project — the map as every other fleet check reads it. Null when the repo has none. */\n architecture: LoadedDoc | null;\n /**\n * Every `services/<id>/` that exists — the census's positive evidence, which\n * is what tells a map's fleet-level box from a container drawn inside a\n * service. Only the bare-last-segment tail join reads it; see `joinIndex`.\n */\n known: ReadonlySet<string>;\n}\n\nexport function divergedFindings(check: DivergenceCheck): Finding[] {\n const { service, model, architecture, known } = check;\n // An extending model declares each of these elements exactly once by\n // construction — there is no copy to diverge — and the shapes' other grade\n // (`c4.element-unowned`) is what asks its question.\n if (model.shape !== \"standalone\") return [];\n if (model.doc.errors.length > 0) return [];\n if (architecture === null || architecture.errors.length > 0) return [];\n\n const index = joinIndex(architecture.elements, known);\n const findings: Finding[] = [];\n for (const mine of model.doc.elements) {\n const theirs = counterpart(index, mine);\n if (theirs === undefined) continue;\n const fields = differing(mine, theirs);\n if (fields.length === 0) continue;\n // Where \"copy the map's declaration verbatim\" is a no-op: the tag sets are\n // all that differ (so the two kinds are equal, and the block to open can be\n // named) and copying the map's literal tags across would leave them still\n // differing. `fields` is checked first because copying DOES fix a title or a\n // binding, so the verbatim remedy stays right whenever one of those differs.\n const specs = { mine: model.doc.specification, theirs: architecture.specification };\n const inherited =\n fields.length === 1 && fields[0] === \"tags\" && copyIsNoOp(mine, theirs, specs)\n ? declaringSide(mine.kind, specs)\n : null;\n findings.push({\n severity: \"warn\",\n code: \"c4.declaration-diverged\",\n subject: service,\n message:\n `${service}: ${ARTIFACT_FILES.model} declares '${mine.id}' as ${describe(mine)} and ` +\n `${MAP} declares ${theirs.id === mine.id ? \"it\" : `'${theirs.id}'`} as ${describe(theirs)} — ` +\n remedy(fields, mine.kind, inherited),\n });\n }\n return findings;\n}\n\n/**\n * What the message says after the two declarations: which fields differ, and\n * what to do about it. `inherited` is null when copying the map's declaration\n * fixes this, and otherwise the clause naming which document's `specification`\n * puts the tags on the kind — the side an author has to open.\n */\nfunction remedy(fields: string[], kind: string, inherited: string | null): string {\n // \"the kind differs\" / \"the title differs\" / \"the tags differ\": the verb\n // agrees with the last field named, and `tags` is the one plural noun in the\n // set. It shipped as \"the kind differ\", and a sentence a reader trips over is\n // one they stop reading.\n const plural = fields.length > 1 || fields[fields.length - 1] === \"tags\";\n const named = `the ${fields.join(\", \")} differ${plural ? \"\" : \"s\"}`;\n // The two kinds are equal whenever `tags` is the only differing field, so the\n // block to open can be named exactly.\n if (inherited !== null) {\n return (\n `${named}, and copying the map's declaration would not clear it: the sets come apart on ` +\n `\\`specification { element ${kind} { … } }\\`, ${inherited}. Either match the two specification ` +\n 'blocks on that kind, or migrate the model to extend the map (SCHEMA.md, \"Two shapes of a service ' +\n 'model\") so there is one specification and one declaration'\n );\n }\n return (\n `${named}, so two documents are two authorities on one element. Copy the map's declaration verbatim, or ` +\n 'migrate the model to extend the map (SCHEMA.md, \"Two shapes of a service model\") so it is declared once'\n );\n}\n\n/** The map's elements, indexed by each key `counterpart` may join on — ambiguous keys dropped. */\ninterface JoinIndex {\n byId: Map<string, Elem>;\n byBinding: Map<string, Elem>;\n bySuffix: Map<string, Elem>;\n}\n\nfunction joinIndex(elements: readonly Elem[], known: ReadonlySet<string>): JoinIndex {\n // Which map elements are the fleet's own boxes rather than somebody's\n // interior, by the ONE predicate every other fleet check asks it with\n // (`fleet/census.ts`): a binding or a title naming a real services/<id>/ makes\n // an element a service at any depth, and what sits inside one is that\n // service's container. Everything else is fleet-level — a top-level element,\n // or a box the map draws inside a plain grouping element — see the header.\n const fleetLevel = new Set(serviceLevelElements([...elements], known).map((e) => e.id));\n return {\n byId: unique(elements, (e) => [e.id]),\n byBinding: unique(elements, (e) => (e.service === undefined ? [] : [e.service as string])),\n // Every DOTTED tail of the id, so a model's `paymentService.api` reaches the\n // map's `marketplace.paymentService.api`: a tail carrying a dot names the\n // owner as well as the leaf, so the two documents are talking about one\n // element.\n //\n // The bare LAST segment names only the leaf, and it is emitted for\n // FLEET-LEVEL boxes alone. This shipped unconditional, and the comment\n // here claimed it was not: a standalone model's own `db`, `api` or `agent`\n // joined whatever `<something>.<that name>` the map happened to hold — one\n // service's private cache told to copy another service's store, verbatim.\n // loam's own meta/docs hit it (`agent` ↔ `loam.core.agent`, a container of\n // src/core/agent/), which is the case that made the rung wrong rather than\n // merely risky (verification 2026-09-04).\n //\n // A box the map draws inside a plain GROUP still emits its bare segment,\n // and deliberately: `infra.cache` is the fleet's cache, so a model's own\n // top-level `cache` is a second meaning for one name and worth the warning.\n // Grouping is a drawing convenience, not ownership — only a SERVICE owns an\n // interior — so the guard is \"no ancestor stands for a service\" and not\n // \"the id carries no dot\".\n bySuffix: unique(elements, (e) => {\n const segments = e.id.split(\".\");\n const tails = segments.map((_, i) => segments.slice(i).join(\".\"));\n return fleetLevel.has(e.id) ? tails : tails.slice(0, -1);\n }),\n };\n}\n\n/** An index holding only the keys exactly ONE element answers to — a shared key joins nothing. */\nfunction unique(elements: readonly Elem[], keys: (e: Elem) => string[]): Map<string, Elem> {\n const claims = new Map<string, Elem[]>();\n for (const element of elements) {\n for (const key of keys(element)) {\n const held = claims.get(key);\n if (held === undefined) claims.set(key, [element]);\n else held.push(element);\n }\n }\n const index = new Map<string, Elem>();\n for (const [key, held] of claims) if (held.length === 1) index.set(key, held[0]!);\n return index;\n}\n\n/** The map's declaration of the element this model declares, or undefined when nothing joins. */\nfunction counterpart(index: JoinIndex, mine: Elem): Elem | undefined {\n const byId = index.byId.get(mine.id);\n if (byId !== undefined) return byId;\n // The binding is the strongest evidence after the id: both documents wrote\n // `metadata { service '<id>' }`, so both mean the same directory.\n const bound = mine.service === undefined ? undefined : index.byBinding.get(mine.service as string);\n if (bound !== undefined) return bound;\n return index.bySuffix.get(mine.id);\n}\n\n/** Which of the four compared fields disagree, in a fixed order so two runs read alike. */\nfunction differing(mine: Elem, theirs: Elem): string[] {\n const fields: string[] = [];\n if (mine.kind !== theirs.kind) fields.push(\"kind\");\n if (mine.title !== theirs.title) fields.push(\"title\");\n // A SET, sorted: tag order is authoring order and means nothing, and a model\n // that lists the map's two tags the other way round is the same declaration.\n if (tagList(mine) !== tagList(theirs)) fields.push(\"tags\");\n if (mine.service !== theirs.service) fields.push(\"service binding\");\n return fields;\n}\n\nfunction tagList(e: Elem): string {\n return [...new Set(e.tags)].sort().join(\" \");\n}\n\n/** The two documents' `specification` blocks, as `copyIsNoOp` and `declaringSide` read them. */\ninterface SpecPair {\n mine: DocSpecification | undefined;\n theirs: DocSpecification | undefined;\n}\n\n/**\n * Would \"copy the map's declaration verbatim\" leave the tag sets still\n * differing? Answered by SIMULATING the copy, which is the only way to be right\n * about it in both directions.\n *\n * `Elem.tags` is what LikeC4 hands over, and since 1.59.0 that includes every\n * tag the element's kind declares (`core/c4/parsed/specification.ts` says why\n * loam reads the block at all). Copying the map's DECLARATION copies its literal\n * tags into the model and nothing else — the model keeps its own specification —\n * so the model's element would come back carrying `mine's kind defaults ∪ the\n * map's literal tags`, against the map's `its kind defaults ∪ the same literal\n * tags`. Those differ exactly when the two kind defaults differ outside the\n * copied literals, and then the remedy is a no-op.\n *\n * The first version compared the two LITERAL tag lists and switched arms only\n * when they were equal, which caught one direction of the same case and missed\n * its mirror: a model writing `#external` on an element whose map twin inherits\n * `#external #platform` from `element topic { … }` was told to copy the map's\n * bare line, and doing exactly that produced the OTHER arm of this same message\n * (verification 2026-09-04, second pass, examples/docs order-service).\n */\nfunction copyIsNoOp(mine: Elem, theirs: Elem, specs: SpecPair): boolean {\n const copied = literalTags(theirs, specs.theirs);\n const after = union(kindTags(mine.kind, specs.mine), copied);\n const before = union(kindTags(theirs.kind, specs.theirs), copied);\n return after !== before;\n}\n\n/**\n * Which document's `specification` puts the tags on the kind — the half of the\n * message that tells an author which file to open. \"Both, differently\" is a real\n * state (each block declares its own tag on the same kind) and gets its own\n * words rather than a guess at one side.\n */\nfunction declaringSide(kind: string, specs: SpecPair): string {\n const mine = kindTags(kind, specs.mine);\n const theirs = kindTags(kind, specs.theirs);\n if (mine.length > 0 && theirs.length > 0) return \"which the two documents declare different tags on\";\n if (theirs.length > 0) return `which ${MAP} declares tags on and ${ARTIFACT_FILES.model} does not`;\n if (mine.length > 0) return `which ${ARTIFACT_FILES.model} declares tags on and ${MAP} does not`;\n // No kind declares anything and the copy still fails: not reachable from the\n // predicate above (with equal kinds and no defaults the copy always clears\n // it), and a sentence that names no file beats one that names the wrong file.\n return \"which the two documents' blocks disagree on\";\n}\n\n/** The tags `spec` puts on every element of `kind`, sorted — the defaults an element cannot see. */\nfunction kindTags(kind: string, spec: DocSpecification | undefined): string[] {\n return [...new Set(spec?.elementKindTags[kind] ?? [])].sort();\n}\n\n/** A tag SET as one comparable string; see `tagList` for why order is dropped. */\nfunction union(a: readonly string[], b: readonly string[]): string {\n return [...new Set([...a, ...b])].sort().join(\" \");\n}\n\nfunction literalTags(e: Elem, spec: DocSpecification | undefined): string[] {\n const inherited = new Set(kindTags(e.kind, spec));\n return [...new Set(e.tags)].filter((tag) => !inherited.has(tag)).sort();\n}\n\n/** One element as the message names it — every compared field, in the order `differing` reports them. */\nfunction describe(e: Elem): string {\n const tags = tagList(e);\n return `${e.kind} '${e.title}' [${tags === \"\" ? \"no tags\" : tags}, bound to ${e.service ?? \"unbound\"}]`;\n}\n"]}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * How a service's `model.likec4` is GRADED, once `c4/service-model/` has
3
+ * decided how to read it.
4
+ *
5
+ * A package of its own beside `../service.ts` because that module is the ORDER
6
+ * — it reads what more than one axis joins through and pushes findings in the
7
+ * sequence the report is read in — and this is one axis's verdicts. Splitting it
8
+ * out is what let the model gain three arms without the order module gaining
9
+ * any: the four states below are the loader's four, and each one has exactly one
10
+ * right answer here.
11
+ *
12
+ * - the map is unreadable (an extending model only): NOTHING. The model could
13
+ * not be read, so neither `c4.invalid` nor `c4.valid` is a claim anybody may
14
+ * make about it, and blaming N services for one broken document is the
15
+ * report. `spine.landscape-invalid` already fires per service and gains a
16
+ * clause saying the model went unread with it (`../spine.ts`).
17
+ * - errors: `c4.invalid`, exactly as before. For an extending model the details
18
+ * name the FILE as well as the line, because a duplicate declaration is
19
+ * reported against the map as well as the model, and a bare `L8:` then points
20
+ * at somebody else's text.
21
+ * - clean, and nothing in the map resolves to this directory:
22
+ * `landscape.service-unmodelled` on the SERVICE target. An extending model
23
+ * says what is inside an element the map binds; with no such element there is
24
+ * nothing to be inside, and every count below would be zero for a file full
25
+ * of architecture.
26
+ * - clean: `c4.valid`, and one `c4.element-unowned` per element the model
27
+ * declared outside its own.
28
+ */
29
+ import { readFile } from "node:fs/promises";
30
+ import { relative } from "node:path";
31
+ import { maskSource } from "../../../../core/c4/source-mask.js";
32
+ import { serviceResolver } from "../../../../core/c4/resolve/service.js";
33
+ import { ARTIFACT_FILES } from "../../../../core/repo/paths.js";
34
+ import { errorText } from "../../../../core/c4/likec4.js";
35
+ export async function modelFindings(grade) {
36
+ const { service, model } = grade;
37
+ // The map did not parse, so the model was never read. Silence here is the
38
+ // whole point: `spine.landscape-invalid` is the one finding, and it names the
39
+ // map rather than blaming a file nobody opened.
40
+ if (model.mapUnreadable)
41
+ return [];
42
+ if (model.doc.errors.length > 0) {
43
+ return [
44
+ {
45
+ severity: "error",
46
+ code: "c4.invalid",
47
+ message: `${service}: C4 model has ${model.doc.errors.length} error(s)`,
48
+ // The hint FIRST, the errors after it: `capDetails` keeps the first ten
49
+ // lines and drops the rest, and a model written in both shapes at once
50
+ // stops resolving every kind and every reference in the file — dozens of
51
+ // cascading errors — so appending the one line that diagnoses it put it
52
+ // past the cap in exactly the case it exists for (verification
53
+ // 2026-09-04). It is the diagnosis; the errors are the evidence.
54
+ details: [...(await extendHint(grade)), ...model.doc.errors.map((err) => detail(err, grade))],
55
+ },
56
+ ];
57
+ }
58
+ if (model.shape === "extending" && !mapBindsAnElement(grade)) {
59
+ return [
60
+ {
61
+ severity: "error",
62
+ code: "landscape.service-unmodelled",
63
+ subject: service,
64
+ message: `${service}: ${ARTIFACT_FILES.model} extends the fleet map, and no element in ` +
65
+ `architecture/landscape.likec4 resolves to ${grade.treePath}/ — nothing in the model can be graded ` +
66
+ `until the map binds one (metadata { service '${service}' })`,
67
+ },
68
+ ];
69
+ }
70
+ const findings = [
71
+ {
72
+ severity: "ok",
73
+ code: "c4.valid",
74
+ message: `${service}: C4 model valid (` +
75
+ (model.shape === "extending" ? "extends the fleet map — " : "") +
76
+ `${model.doc.elements.length} elements · ${model.doc.relationships.length} relationships)` +
77
+ // Outside the parentheses because the counts inside are facts about
78
+ // model.likec4 alone and the flows are read from the whole project.
79
+ (grade.gradedFlows === 0 ? "" : ` · ${grade.gradedFlows} tagged flow(s) graded`),
80
+ },
81
+ ];
82
+ for (const element of model.unowned) {
83
+ findings.push({
84
+ severity: "warn",
85
+ code: "c4.element-unowned",
86
+ subject: service,
87
+ message: `${service}: ${ARTIFACT_FILES.model} declares '${element.id}' (${element.kind}) outside this service's ` +
88
+ "own element — an extending model adds elements under the element that resolves to " +
89
+ `${service} only. Three remedies, and the FIRST is the one a store or a component usually ` +
90
+ // The nested arm led the other two from 2026-09-04: a private store's own
91
+ // remedy was missing here, so following what this printed moved a store
92
+ // to fleet level and earned `landscape.service-undocumented` (#01/W8).
93
+ `wants: a store or component this service owns goes INSIDE the \`extend <fqn> { }\` block, ` +
94
+ "where its id becomes `<fqn>.<name>`; a system this service reaches belongs in " +
95
+ "architecture/landscape.likec4, declared once; and another service's internals belong in " +
96
+ "that service's model",
97
+ });
98
+ }
99
+ return findings;
100
+ }
101
+ /**
102
+ * Does anything in the per-service project resolve to this service?
103
+ *
104
+ * The same question `sliceForService` asks to build `own`, asked again rather
105
+ * than carried on the `ServiceModel`: the slice publishes what a check READS
106
+ * (the elements, the edges, the strays), and "was the set empty" is a fact
107
+ * about the map's bindings rather than a fourth list. Resolved over the
108
+ * PROJECT's elements, because that is the index the slice used and a
109
+ * nearest-ancestor binding must win here exactly as it does there.
110
+ *
111
+ * A standalone model never reaches this: it declares its own elements and
112
+ * answers for them whether or not the map has heard of the directory — the
113
+ * fleet target's own `landscape.service-unmodelled` is where that is said.
114
+ */
115
+ function mapBindsAnElement(grade) {
116
+ const project = grade.model.project;
117
+ if (project === null)
118
+ return true;
119
+ const resolve = serviceResolver(project.elements, grade.known);
120
+ return project.elements.some((e) => resolve(e.id) === grade.service);
121
+ }
122
+ /**
123
+ * One parse error, spelled for the shape it came from.
124
+ *
125
+ * A standalone model is one file, so its errors keep the bare `L8: …` form they
126
+ * have always had — every fixture and every reader of that message is pinned to
127
+ * it. An extending model is a PROJECT, and LikeC4 reports a duplicate
128
+ * declaration against BOTH documents: without the file name the reader is sent
129
+ * to a line number in a file they were never told about, which is the exact
130
+ * failure `landscape.invalid` fixed one altitude up.
131
+ */
132
+ function detail(err, grade) {
133
+ if (grade.model.shape === "standalone" || err.sourceFsPath === undefined)
134
+ return errorText(err);
135
+ return `${relative(grade.docsDir, err.sourceFsPath).split(/[\\/]/).join("/")} ${errorText(err)}`;
136
+ }
137
+ /** An `extend` in a masked model — the bytes, not a parse, because the parse is what failed. */
138
+ const EXTENDS = /\bextend\s+[A-Za-z_][\w.-]*\s*\{/;
139
+ /**
140
+ * The one line that explains a model written in BOTH shapes at once.
141
+ *
142
+ * A file declaring an element kind is standalone (SCHEMA.md, "Two shapes of a
143
+ * service model"), so it is parsed alone — and every `extend <fqn>` in it then
144
+ * resolves nothing, because the fqn belongs to a map this load never opened.
145
+ * LikeC4 says `Could not resolve reference to Element named '<fqn>'`, which
146
+ * reads as a typo; nothing said which of the two lines the author has to drop
147
+ * (verification 2026-09-04, D10). Emitted only for the standalone shape, where
148
+ * the diagnosis is certain; an unreadable file adds nothing, since the loader
149
+ * already answered `standalone` for the same reason and `c4.invalid` names it.
150
+ */
151
+ async function extendHint(grade) {
152
+ if (grade.model.shape !== "standalone")
153
+ return [];
154
+ const text = await readFile(grade.modelPath, "utf8").catch(() => null);
155
+ if (text === null || !EXTENDS.test(maskSource(text).code))
156
+ return [];
157
+ return [
158
+ `${ARTIFACT_FILES.model} declares an element kind in \`specification { }\`, so it is read as the ` +
159
+ "standalone shape — parsed alone, where `extend` resolves nothing. Drop the kind declaration to " +
160
+ "extend the fleet map, or drop the `extend` and declare the elements here",
161
+ ];
162
+ }
163
+ //# sourceMappingURL=grade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grade.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/model/grade.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How a service's `model.likec4` is GRADED, once `c4/service-model/` has\n * decided how to read it.\n *\n * A package of its own beside `../service.ts` because that module is the ORDER\n * — it reads what more than one axis joins through and pushes findings in the\n * sequence the report is read in — and this is one axis's verdicts. Splitting it\n * out is what let the model gain three arms without the order module gaining\n * any: the four states below are the loader's four, and each one has exactly one\n * right answer here.\n *\n * - the map is unreadable (an extending model only): NOTHING. The model could\n * not be read, so neither `c4.invalid` nor `c4.valid` is a claim anybody may\n * make about it, and blaming N services for one broken document is the\n * report. `spine.landscape-invalid` already fires per service and gains a\n * clause saying the model went unread with it (`../spine.ts`).\n * - errors: `c4.invalid`, exactly as before. For an extending model the details\n * name the FILE as well as the line, because a duplicate declaration is\n * reported against the map as well as the model, and a bare `L8:` then points\n * at somebody else's text.\n * - clean, and nothing in the map resolves to this directory:\n * `landscape.service-unmodelled` on the SERVICE target. An extending model\n * says what is inside an element the map binds; with no such element there is\n * nothing to be inside, and every count below would be zero for a file full\n * of architecture.\n * - clean: `c4.valid`, and one `c4.element-unowned` per element the model\n * declared outside its own.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\nimport { maskSource } from \"../../../../core/c4/source-mask.js\";\nimport type { ServiceModel } from \"../../../../core/c4/service-model/load.js\";\nimport { serviceResolver } from \"../../../../core/c4/resolve/service.js\";\nimport type { LikeC4Error } from \"../../../../core/c4/likec4.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\nimport type { PathableService } from \"../../../../core/kernel/ids/service.js\";\nimport { ARTIFACT_FILES } from \"../../../../core/repo/paths.js\";\nimport type { Finding } from \"../../../../core/vocabulary/report.js\";\nimport { errorText } from \"../../../../core/c4/likec4.js\";\n\nexport interface ModelGrade {\n service: PathableService;\n /** Repo-relative, `/`-separated — the directory the map has to bind, named in the unmodelled arm. */\n treePath: string;\n docsDir: DocsDir;\n model: ServiceModel;\n /** Absolute path of `model.likec4` — read only to explain a parse error, see `extendHint`. */\n modelPath: string;\n /** Every service directory that exists — the resolver's positive evidence, exactly as the slice used it. */\n known: ReadonlySet<string>;\n /**\n * How many of this service's own `dynamic view`s carried a reserved tag and\n * were graded (`core/usecases/service/flows.ts`), counted by the caller\n * because the scan is the use-case axis's and this is the model's.\n *\n * It rides on `c4.valid` and nowhere else. A healthy intra-service flow used\n * to be invisible on every loam surface — the axis reports only what is wrong\n * with one — so a team that wrote the slot correctly could not tell it from a\n * team whose views were never read (verification 2026-09-04, D10).\n */\n gradedFlows: number;\n}\n\nexport async function modelFindings(grade: ModelGrade): Promise<Finding[]> {\n const { service, model } = grade;\n // The map did not parse, so the model was never read. Silence here is the\n // whole point: `spine.landscape-invalid` is the one finding, and it names the\n // map rather than blaming a file nobody opened.\n if (model.mapUnreadable) return [];\n\n if (model.doc.errors.length > 0) {\n return [\n {\n severity: \"error\",\n code: \"c4.invalid\",\n message: `${service}: C4 model has ${model.doc.errors.length} error(s)`,\n // The hint FIRST, the errors after it: `capDetails` keeps the first ten\n // lines and drops the rest, and a model written in both shapes at once\n // stops resolving every kind and every reference in the file — dozens of\n // cascading errors — so appending the one line that diagnoses it put it\n // past the cap in exactly the case it exists for (verification\n // 2026-09-04). It is the diagnosis; the errors are the evidence.\n details: [...(await extendHint(grade)), ...model.doc.errors.map((err) => detail(err, grade))],\n },\n ];\n }\n\n if (model.shape === \"extending\" && !mapBindsAnElement(grade)) {\n return [\n {\n severity: \"error\",\n code: \"landscape.service-unmodelled\",\n subject: service,\n message:\n `${service}: ${ARTIFACT_FILES.model} extends the fleet map, and no element in ` +\n `architecture/landscape.likec4 resolves to ${grade.treePath}/ — nothing in the model can be graded ` +\n `until the map binds one (metadata { service '${service}' })`,\n },\n ];\n }\n\n const findings: Finding[] = [\n {\n severity: \"ok\",\n code: \"c4.valid\",\n message:\n `${service}: C4 model valid (` +\n (model.shape === \"extending\" ? \"extends the fleet map — \" : \"\") +\n `${model.doc.elements.length} elements · ${model.doc.relationships.length} relationships)` +\n // Outside the parentheses because the counts inside are facts about\n // model.likec4 alone and the flows are read from the whole project.\n (grade.gradedFlows === 0 ? \"\" : ` · ${grade.gradedFlows} tagged flow(s) graded`),\n },\n ];\n for (const element of model.unowned) {\n findings.push({\n severity: \"warn\",\n code: \"c4.element-unowned\",\n subject: service,\n message:\n `${service}: ${ARTIFACT_FILES.model} declares '${element.id}' (${element.kind}) outside this service's ` +\n \"own element — an extending model adds elements under the element that resolves to \" +\n `${service} only. Three remedies, and the FIRST is the one a store or a component usually ` +\n // The nested arm led the other two from 2026-09-04: a private store's own\n // remedy was missing here, so following what this printed moved a store\n // to fleet level and earned `landscape.service-undocumented` (#01/W8).\n `wants: a store or component this service owns goes INSIDE the \\`extend <fqn> { }\\` block, ` +\n \"where its id becomes `<fqn>.<name>`; a system this service reaches belongs in \" +\n \"architecture/landscape.likec4, declared once; and another service's internals belong in \" +\n \"that service's model\",\n });\n }\n return findings;\n}\n\n/**\n * Does anything in the per-service project resolve to this service?\n *\n * The same question `sliceForService` asks to build `own`, asked again rather\n * than carried on the `ServiceModel`: the slice publishes what a check READS\n * (the elements, the edges, the strays), and \"was the set empty\" is a fact\n * about the map's bindings rather than a fourth list. Resolved over the\n * PROJECT's elements, because that is the index the slice used and a\n * nearest-ancestor binding must win here exactly as it does there.\n *\n * A standalone model never reaches this: it declares its own elements and\n * answers for them whether or not the map has heard of the directory — the\n * fleet target's own `landscape.service-unmodelled` is where that is said.\n */\nfunction mapBindsAnElement(grade: ModelGrade): boolean {\n const project = grade.model.project;\n if (project === null) return true;\n const resolve = serviceResolver(project.elements, grade.known);\n return project.elements.some((e) => resolve(e.id) === (grade.service as string));\n}\n\n/**\n * One parse error, spelled for the shape it came from.\n *\n * A standalone model is one file, so its errors keep the bare `L8: …` form they\n * have always had — every fixture and every reader of that message is pinned to\n * it. An extending model is a PROJECT, and LikeC4 reports a duplicate\n * declaration against BOTH documents: without the file name the reader is sent\n * to a line number in a file they were never told about, which is the exact\n * failure `landscape.invalid` fixed one altitude up.\n */\nfunction detail(err: LikeC4Error, grade: ModelGrade): string {\n if (grade.model.shape === \"standalone\" || err.sourceFsPath === undefined) return errorText(err);\n return `${relative(grade.docsDir, err.sourceFsPath).split(/[\\\\/]/).join(\"/\")} ${errorText(err)}`;\n}\n\n/** An `extend` in a masked model — the bytes, not a parse, because the parse is what failed. */\nconst EXTENDS = /\\bextend\\s+[A-Za-z_][\\w.-]*\\s*\\{/;\n\n/**\n * The one line that explains a model written in BOTH shapes at once.\n *\n * A file declaring an element kind is standalone (SCHEMA.md, \"Two shapes of a\n * service model\"), so it is parsed alone — and every `extend <fqn>` in it then\n * resolves nothing, because the fqn belongs to a map this load never opened.\n * LikeC4 says `Could not resolve reference to Element named '<fqn>'`, which\n * reads as a typo; nothing said which of the two lines the author has to drop\n * (verification 2026-09-04, D10). Emitted only for the standalone shape, where\n * the diagnosis is certain; an unreadable file adds nothing, since the loader\n * already answered `standalone` for the same reason and `c4.invalid` names it.\n */\nasync function extendHint(grade: ModelGrade): Promise<string[]> {\n if (grade.model.shape !== \"standalone\") return [];\n const text = await readFile(grade.modelPath, \"utf8\").catch(() => null);\n if (text === null || !EXTENDS.test(maskSource(text).code)) return [];\n return [\n `${ARTIFACT_FILES.model} declares an element kind in \\`specification { }\\`, so it is read as the ` +\n \"standalone shape — parsed alone, where `extend` resolves nothing. Drop the kind declaration to \" +\n \"extend the fleet map, or drop the `extend` and declare the elements here\",\n ];\n}\n"]}
@@ -11,35 +11,38 @@
11
11
  */
12
12
  import { existsSync } from "node:fs";
13
13
  import { relative } from "node:path";
14
- import { loadFile } from "../../../core/c4/likec4.js";
14
+ import {} from "../../../core/c4/likec4.js";
15
+ import {} from "../../../core/c4/service-model/load.js";
15
16
  import { serviceResolver } from "../../../core/c4/resolve/service.js";
16
17
  import {} from "../../../core/kernel/ids/service.js";
17
18
  import { landscapePath as landscapeFile, permissionsPath } from "../../../core/repo/paths.js";
18
19
  import { locateServicePaths } from "../../../core/repo/service-target.js";
19
20
  import { readVocabulary } from "../../../core/permissions/permissions.js";
20
- import { readCapabilityVocabulary } from "../../../core/capabilities/capabilities.js";
21
- import { capabilityRequirementIndex, capabilityUnknownFindings } from "../../../core/capabilities/findings.js";
22
- import { realizesStaleFindings, realizesUnknownFindings } from "../../../core/capabilities/realizes/findings.js";
23
- import { parseRequirements, readRequirementsDocument } from "../../../core/document/parse.js";
21
+ import { capabilityRequirementIndex, capabilityUnknownIssues } from "../../../core/capabilities/findings.js";
22
+ import { realizesStaleFindings, realizesUnknownIssues } from "../../../core/capabilities/realizes/findings.js";
24
23
  import { requiresUnknownFindings } from "../checks/requirements.js";
25
24
  import { listServices } from "../../../core/repo/repo.js";
26
- import {} from "../../../core/c4/likec4.js";
27
25
  import {} from "../../../core/vocabulary/report.js";
28
- import { closeIds } from "../../../core/c4/arch.js";
29
26
  import { serviceProvenance } from "../../../core/provenance/findings.js";
30
27
  import { gherkinFindings } from "../../../core/gherkin/stale.js";
31
28
  import { gherkinRoot } from "../../../core/gherkin/stamp.js";
32
29
  import { UnsafePathError } from "../../../core/kernel/path-safety.js";
30
+ import { repoPath } from "../../../core/envelope/json.js";
33
31
  import { interruptedCommitFinding } from "../../../core/staging/recovery/finding.js";
34
32
  import { FleetContext } from "../../../core/fleet-context.js";
35
- import { errorText } from "../checks/vocabulary.js";
36
33
  import { serviceLinkFindings } from "../links/corpus.js";
37
34
  import { sourceScopeFindings } from "../checks/sources.js";
38
- import { apiAxisFindings, generatedContractFindings } from "./api.js";
35
+ import { unknownServiceFinding } from "../checks/introduced.js";
36
+ import { apiAxisFindings } from "./api.js";
37
+ import { generatedContractFindings } from "./contracts/generated.js";
39
38
  import { evidencePinFindings } from "./evidence-pins.js";
40
39
  import { spineFindings } from "./spine.js";
41
40
  import { eventAxisFindings } from "./events/events.js";
42
41
  import { archAxisFindings, readServiceSpecs } from "./specs.js";
42
+ import { modelFindings } from "./model/grade.js";
43
+ import { divergedFindings } from "./model/diverged.js";
44
+ import { requirementIdsOf, serviceUseCaseFindings } from "./usecases/usecases.js";
45
+ import { readServiceFlows } from "../../../core/usecases/service/flows.js";
43
46
  import { unknownDirectiveFindings } from "../../../core/document/grammar/directives.js";
44
47
  export async function validateService(check) {
45
48
  const { docsDir, service, repoDir, preloaded, gherkinDir, contracts, fleet } = check;
@@ -53,27 +56,32 @@ export async function validateService(check) {
53
56
  const me = service;
54
57
  const findings = [];
55
58
  const paths = await locateServicePaths(docsDir, service, fleet);
59
+ // Repo-relative, `/`-separated: the report's own scope, and the spelling
60
+ // every service-local use-case finding opens with.
61
+ const treePath = relative(docsDir, paths.dir).replaceAll("\\", "/");
56
62
  const report = {
57
63
  kind: "service",
58
64
  id: service,
59
- path: relative(docsDir, paths.dir).replaceAll("\\", "/"),
65
+ path: treePath,
60
66
  findings,
61
67
  };
62
68
  // A directory that does not exist is a different fact from a directory with
63
- // everything missing: validating a typo must say "typo", not "unadopted".
69
+ // everything missing: validating a typo must say "typo", not "unadopted" —
70
+ // and a service a feature in flight introduces is neither (checks/introduced.ts).
64
71
  if (!existsSync(paths.dir)) {
65
- const close = closeIds(service, (await listServices(docsDir, fleet)).map((s) => s.id));
66
- findings.push({
67
- severity: "error",
68
- code: "service.unknown",
69
- message: `No service directory at ${paths.dir}.` +
70
- (close.length > 0
71
- ? ` Did you mean: ${close.join(", ")}?`
72
- : " `loam list services` shows what exists."),
73
- text: { marker: false },
74
- });
72
+ findings.push(await unknownServiceFinding(docsDir, service, treePath, fleet));
75
73
  return report;
76
74
  }
75
+ // Which service directories actually exist — the positive evidence
76
+ // `serviceOf` needs to resolve an edge drawn into a modelled CONTAINER
77
+ // (`paymentService.api`) back to the service that owns it. Without it every
78
+ // such edge resolves to the container's own title, i.e. to a service nobody
79
+ // has ever adopted, and drops out of the spine unnoticed.
80
+ //
81
+ // Read before the model rather than after it, because the model is now read
82
+ // through the same resolver: an extending model's own slice is exactly the
83
+ // elements this set makes resolvable.
84
+ const known = new Set((await listServices(docsDir, fleet)).map((s) => s.id));
77
85
  // C4 model. Its absence is an error — this is where `adopt` comes in — but it
78
86
  // must NOT silence the rest of the gate stack: `loam archive` of a feature
79
87
  // introducing a new service creates exactly this state (spec.md, arch.spec.md,
@@ -85,58 +93,84 @@ export async function validateService(check) {
85
93
  const hasModel = existsSync(paths.model);
86
94
  let elements = [];
87
95
  let relationships = [];
96
+ // The model as its own SHAPE demands it be read — a file parsed alone, or a
97
+ // slice of the project it extends (`core/c4/service-model/load.ts`). Kept for
98
+ // the service's own use cases below, which need both its views and which
99
+ // shape produced them.
100
+ let model = null;
101
+ // The service's OWN use cases, scanned here rather than beside the grades it
102
+ // feeds: `c4.valid` reports how many were graded, and that count is the only
103
+ // surface a HEALTHY flow appears on. Null means no scan was made — the model
104
+ // does not parse (`c4.invalid` owns that, and a second load repeats its
105
+ // cascade under a use-case code) or its map does not (an extending model then
106
+ // comes back with no errors AND no model, and the flow project would be
107
+ // staged over the same broken map).
108
+ let flows = null;
88
109
  if (!hasModel) {
89
110
  findings.push({
90
111
  severity: "error",
91
112
  code: "service.no-model",
92
- message: `No C4 model at ${paths.model}. Run \`loam adopt\` for '${service}' first.`,
93
- text: { marker: false },
113
+ message: `No C4 model at ${repoPath(docsDir, paths.model)}. Run \`loam adopt\` for '${service}' first.`,
94
114
  });
95
115
  }
96
116
  else {
97
- const model = fleet === undefined ? await loadFile(paths.model) : await fleet.loadLikeC4(paths.model);
98
- elements = model.elements;
99
- relationships = model.relationships;
100
- if (model.errors.length > 0) {
101
- findings.push({
102
- severity: "error",
103
- code: "c4.invalid",
104
- message: `${service}: C4 model has ${model.errors.length} error(s)`,
105
- details: model.errors.map(errorText),
106
- });
107
- }
108
- else {
109
- findings.push({
110
- severity: "ok",
111
- code: "c4.valid",
112
- message: `${service}: C4 model valid (${elements.length} elements · ${relationships.length} relationships)`,
117
+ model = await fleet.serviceModel(docsDir, paths);
118
+ elements = model.doc.elements;
119
+ relationships = model.doc.relationships;
120
+ if (!model.mapUnreadable && model.doc.errors.length === 0) {
121
+ flows = await readServiceFlows({
122
+ paths,
123
+ model: model.doc,
124
+ known,
125
+ // An extending model's siblings resolve against the fleet map, so their
126
+ // project is the docs root's, never the service directory. The project
127
+ // the model was READ in travels with it because that is the scope of a
128
+ // view written inside model.likec4: `model.doc` is the service's own
129
+ // slice, and a hop backed by one of the map's edges is backed wherever
130
+ // the view sits.
131
+ ...(model.shape === "extending" && model.project !== null
132
+ ? { extending: { docsDir, project: model.project } }
133
+ : {}),
113
134
  });
114
135
  }
136
+ findings.push(...(await modelFindings({
137
+ service,
138
+ treePath,
139
+ docsDir,
140
+ model,
141
+ modelPath: paths.model,
142
+ known,
143
+ gradedFlows: flows?.kind === "read" ? flows.views.length : 0,
144
+ })));
115
145
  }
116
- // The living landscape, parsed at most once per run: under --all the caller
117
- // hands in the doc it already loaded, single-service runs load on demand. It
118
- // serves two checks below — the no-openapi grace and the spine.
119
- const land = preloaded ??
120
- (existsSync(landscapeFile(docsDir))
121
- ? fleet === undefined
122
- ? await loadFile(landscapeFile(docsDir))
123
- : await fleet.loadLikeC4(landscapeFile(docsDir))
124
- : null);
125
- // Which service directories actually exist — the positive evidence
126
- // `serviceOf` needs to resolve an edge drawn into a modelled CONTAINER
127
- // (`paymentService.api`) back to the service that owns it. Without it every
128
- // such edge resolves to the container's own title, i.e. to a service nobody
129
- // has ever adopted, and drops out of the spine unnoticed.
130
- const known = new Set((await listServices(docsDir, fleet)).map((s) => s.id));
146
+ // The fleet map as the PROJECT it is, in EVERY mode — landscape plus every
147
+ // `architecture/**/*.likec4` minus the generated views file. Single-service
148
+ // runs used to read `landscape.likec4` alone, which made three answers depend
149
+ // on which flag the caller happened to pass: a use-case file that does not
150
+ // parse was `spine.landscape-invalid` under `--all` and silence under
151
+ // `--service`, an extending model was readable under one and not the other,
152
+ // and `c4.declaration-diverged` would have compared against a different
153
+ // document. Under `--all` the caller hands in the doc it already loaded.
154
+ //
155
+ // Still gated on the landscape FILE existing: `land === null` means "this repo
156
+ // draws no fleet", and a repo with an `architecture/usecases/` and no map is
157
+ // that state, not a map that loaded empty.
158
+ const land = preloaded ?? (existsSync(landscapeFile(docsDir)) ? await fleet.architecture(docsDir) : null);
159
+ // Where a standalone model's copies of the map's elements have drifted. After
160
+ // `c4.valid` because it is a claim about a model that parsed, and silent for
161
+ // an extending model, which has no copy to diverge.
162
+ if (model !== null)
163
+ findings.push(...divergedFindings({ service, model, architecture: land, known }));
131
164
  // The living landscape's element→service resolver, container-aware and
132
165
  // memoized. Every question below that asks "does this edge point at me?"
133
166
  // asks it through this one function, so the no-openapi grace, the HTTP spine
134
167
  // and the event spine can never disagree about which edges are inbound.
135
168
  const landSvcOf = land === null ? null : serviceResolver(land.elements, known);
136
- const specs = await readServiceSpecs({ service, paths, fleet });
169
+ const specs = await readServiceSpecs({ docsDir, service, paths, fleet });
137
170
  findings.push(...specs.findings);
138
171
  const { reqs, livingReqs, archText, archReqs } = specs;
139
172
  const api = await apiAxisFindings({
173
+ docsDir,
140
174
  service,
141
175
  me,
142
176
  paths,
@@ -149,11 +183,16 @@ export async function validateService(check) {
149
183
  findings.push(...api.findings);
150
184
  findings.push(...spineFindings({
151
185
  service,
186
+ docsDir,
187
+ treePath,
152
188
  me,
153
189
  land,
154
190
  landSvcOf,
155
191
  contract: api.contract,
156
192
  landscapeReported: check.landscapeReported,
193
+ // An unreadable map takes an extending model with it, and the spine
194
+ // finding is where that is said — see `Spine.modelExtends`.
195
+ modelExtends: model?.shape === "extending",
157
196
  }));
158
197
  findings.push(...(await eventAxisFindings({
159
198
  docsDir,
@@ -175,29 +214,50 @@ export async function validateService(check) {
175
214
  archReqs,
176
215
  elements,
177
216
  relationships,
217
+ // The one fact that tells "this service models nothing" apart from "this
218
+ // service's model could not be read" — its map does not parse, or it does
219
+ // not itself (`c4.invalid` above): both arrive here as empty lists, and
220
+ // only the second one must silence `covers.unknown`.
221
+ modelUnreadable: model !== null && (model.mapUnreadable || model.doc.errors.length > 0),
178
222
  land,
179
223
  known,
224
+ docsDir,
225
+ fleet,
180
226
  })));
227
+ // The service's own use cases, GRADED — scanned beside the model above. Here
228
+ // rather than on the fleet target because the hops name this service's
229
+ // containers, which only its project resolves; after the arch axis because a
230
+ // `#req-` tag resolves against the ids both spec documents declare.
231
+ if (flows !== null) {
232
+ findings.push(...serviceUseCaseFindings({
233
+ service,
234
+ treePath,
235
+ docsDir,
236
+ scan: flows,
237
+ services: known,
238
+ requirementIds: requirementIdsOf(paths, livingReqs, archReqs),
239
+ }));
240
+ }
181
241
  // The authorization axis. Both requirement documents are graded against one
182
242
  // fleet vocabulary — a permission is a fleet fact, and an arch requirement
183
243
  // gates on one exactly as a business requirement does. The capability axis
184
244
  // rides the same loop against ITS fleet vocabulary; under --all the fleet
185
245
  // memo makes that one parse for the whole run.
186
246
  const vocabulary = await readVocabulary(permissionsPath(docsDir));
187
- const capabilities = fleet === undefined ? await readCapabilityVocabulary(docsDir) : await fleet.capabilities(docsDir);
247
+ const capabilities = await fleet.capabilities(docsDir);
188
248
  // The requirement-level index behind `Realizes:`. Built once for both axes,
189
- // and behind the same ladder `capabilityUnknownFindings` applies — an absent
249
+ // and behind the same ladder `capabilityUnknownIssues` applies — an absent
190
250
  // or unreadable vocabulary means no reads and no grades at all, so a fleet
191
251
  // that has not adopted the axis pays nothing for a line it never wrote.
192
- const capabilityReqs = await capabilityRequirementIndex(capabilities, fleet === undefined ? async (p) => parseRequirements(await readRequirementsDocument(p)) : (p) => fleet.readRequirements(p));
252
+ const capabilityReqs = await capabilityRequirementIndex(capabilities, (p) => fleet.readRequirements(p));
193
253
  for (const [label, docReqs] of [
194
254
  ["spec.md", reqs],
195
255
  ["arch.spec.md", archReqs],
196
256
  ]) {
197
257
  const target = { where: `${service}: ${label}`, subject: service };
198
258
  findings.push(...requiresUnknownFindings(docReqs, target, vocabulary));
199
- findings.push(...capabilityUnknownFindings(docReqs, target, capabilities));
200
- findings.push(...realizesUnknownFindings(docReqs, target, capabilityReqs));
259
+ findings.push(...capabilityUnknownIssues(docReqs, target, capabilities));
260
+ findings.push(...realizesUnknownIssues(docReqs, target, capabilityReqs));
201
261
  findings.push(...realizesStaleFindings(docReqs, target, capabilityReqs));
202
262
  // The grammar guard, last in the loop: it grades the KEYS the three checks
203
263
  // above resolve values from, so a line none of them saw is reported here
@@ -210,7 +270,7 @@ export async function validateService(check) {
210
270
  // document checks and adds no input any of them depend on.
211
271
  findings.push(...(await serviceLinkFindings({ docsDir, subject: service, fleet }, paths)));
212
272
  // Provenance last: who vouched for this, and what code it was written from.
213
- findings.push(...(await serviceProvenance(docsDir, service, { repoDir, fleet })));
273
+ findings.push(...(await serviceProvenance(docsDir, service, { repoDir, loamOwned: check.loamOwned, fleet })));
214
274
  findings.push(...(await sourceScopeFindings(docsDir, service, repoDir, fleet)));
215
275
  // The build's own contract against the committed copy. Service-repo only:
216
276
  // from the docs repo there is no build output to read, and the family stays