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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1036) hide show
  1. package/CHANGELOG.md +2635 -86
  2. package/COMPARISON.md +80 -63
  3. package/CONTRIBUTING.md +84 -0
  4. package/MIGRATING-from-OpenSpec.md +213 -54
  5. package/QUICKSTART.md +125 -0
  6. package/README.md +450 -150
  7. package/ROADMAP.md +599 -0
  8. package/SCHEMA.md +2355 -256
  9. package/SECURITY.md +21 -3
  10. package/WORKFLOW.md +701 -0
  11. package/dist/cli.js +113 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/adopt/adopt.js +209 -0
  14. package/dist/commands/adopt/adopt.js.map +1 -0
  15. package/dist/commands/adopt/render.js +268 -0
  16. package/dist/commands/adopt/render.js.map +1 -0
  17. package/dist/commands/archive/archive.js +77 -0
  18. package/dist/commands/archive/archive.js.map +1 -0
  19. package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
  20. package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
  21. package/dist/commands/archive/plan/contracts/openapi.js +159 -0
  22. package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
  23. package/dist/commands/archive/plan/gate.js +323 -0
  24. package/dist/commands/archive/plan/gate.js.map +1 -0
  25. package/dist/commands/archive/plan/landscape.js +180 -0
  26. package/dist/commands/archive/plan/landscape.js.map +1 -0
  27. package/dist/commands/archive/plan/refusal.js +111 -0
  28. package/dist/commands/archive/plan/refusal.js.map +1 -0
  29. package/dist/commands/archive/plan/specs.js +237 -0
  30. package/dist/commands/archive/plan/specs.js.map +1 -0
  31. package/dist/commands/archive/plan/state.js +27 -0
  32. package/dist/commands/archive/plan/state.js.map +1 -0
  33. package/dist/commands/archive/run.js +252 -0
  34. package/dist/commands/archive/run.js.map +1 -0
  35. package/dist/commands/context/context.js +99 -0
  36. package/dist/commands/context/context.js.map +1 -0
  37. package/dist/commands/context/print.js +283 -0
  38. package/dist/commands/context/print.js.map +1 -0
  39. package/dist/commands/delta/delta.js +279 -0
  40. package/dist/commands/delta/delta.js.map +1 -0
  41. package/dist/commands/delta/print.js +205 -0
  42. package/dist/commands/delta/print.js.map +1 -0
  43. package/dist/commands/dependencies.js +9 -7
  44. package/dist/commands/dependencies.js.map +1 -1
  45. package/dist/commands/diff/diff.js +93 -0
  46. package/dist/commands/diff/diff.js.map +1 -0
  47. package/dist/commands/diff/print.js +38 -0
  48. package/dist/commands/diff/print.js.map +1 -0
  49. package/dist/commands/doctor.js +5 -4
  50. package/dist/commands/doctor.js.map +1 -1
  51. package/dist/commands/explain/explain.js +151 -0
  52. package/dist/commands/explain/explain.js.map +1 -0
  53. package/dist/commands/explore.js +29 -24
  54. package/dist/commands/explore.js.map +1 -1
  55. package/dist/commands/gate/gate.js +115 -0
  56. package/dist/commands/gate/gate.js.map +1 -0
  57. package/dist/commands/gate/print.js +54 -0
  58. package/dist/commands/gate/print.js.map +1 -0
  59. package/dist/commands/gherkin/commit.js +146 -0
  60. package/dist/commands/gherkin/commit.js.map +1 -0
  61. package/dist/commands/gherkin/gherkin.js +246 -0
  62. package/dist/commands/gherkin/gherkin.js.map +1 -0
  63. package/dist/commands/gherkin/reconcile.js +149 -0
  64. package/dist/commands/gherkin/reconcile.js.map +1 -0
  65. package/dist/commands/gherkin/render.js +77 -0
  66. package/dist/commands/gherkin/render.js.map +1 -0
  67. package/dist/commands/init/example.js +204 -0
  68. package/dist/commands/init/example.js.map +1 -0
  69. package/dist/commands/init/first-hour.js +28 -0
  70. package/dist/commands/init/first-hour.js.map +1 -0
  71. package/dist/commands/{init.js → init/init.js} +146 -71
  72. package/dist/commands/init/init.js.map +1 -0
  73. package/dist/commands/init/mcp.js +131 -0
  74. package/dist/commands/init/mcp.js.map +1 -0
  75. package/dist/commands/init/options.js +78 -0
  76. package/dist/commands/init/options.js.map +1 -0
  77. package/dist/commands/instructions.js +72 -14
  78. package/dist/commands/instructions.js.map +1 -1
  79. package/dist/commands/list/campaign/campaign.js +65 -0
  80. package/dist/commands/list/campaign/campaign.js.map +1 -0
  81. package/dist/commands/list/campaign/owners.js +65 -0
  82. package/dist/commands/list/campaign/owners.js.map +1 -0
  83. package/dist/commands/list/json.js +157 -0
  84. package/dist/commands/list/json.js.map +1 -0
  85. package/dist/commands/list/list.js +343 -0
  86. package/dist/commands/list/list.js.map +1 -0
  87. package/dist/commands/list/print.js +301 -0
  88. package/dist/commands/list/print.js.map +1 -0
  89. package/dist/commands/list/review.js +120 -0
  90. package/dist/commands/list/review.js.map +1 -0
  91. package/dist/commands/list/views.js +105 -0
  92. package/dist/commands/list/views.js.map +1 -0
  93. package/dist/commands/mcp/dispatch.js +183 -0
  94. package/dist/commands/mcp/dispatch.js.map +1 -0
  95. package/dist/commands/mcp/mcp.js +34 -0
  96. package/dist/commands/mcp/mcp.js.map +1 -0
  97. package/dist/commands/mcp/serve.js +55 -0
  98. package/dist/commands/mcp/serve.js.map +1 -0
  99. package/dist/commands/migrate-openspec/materialize/active.js +170 -0
  100. package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
  101. package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
  102. package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
  103. package/dist/commands/migrate-openspec/materialize/living.js +62 -0
  104. package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
  105. package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
  106. package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
  107. package/dist/commands/migrate-openspec/materialize/target.js +219 -0
  108. package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
  109. package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
  110. package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
  111. package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
  112. package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
  113. package/dist/commands/migrate-openspec/openspec/error.js +19 -0
  114. package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
  115. package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
  116. package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
  117. package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
  118. package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
  119. package/dist/commands/migrate-openspec/openspec/read.js +49 -0
  120. package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
  121. package/dist/commands/migrate-openspec/print.js +89 -0
  122. package/dist/commands/migrate-openspec/print.js.map +1 -0
  123. package/dist/commands/new/commit.js +76 -0
  124. package/dist/commands/new/commit.js.map +1 -0
  125. package/dist/commands/new/new.js +283 -0
  126. package/dist/commands/new/new.js.map +1 -0
  127. package/dist/commands/new/templates.js +355 -0
  128. package/dist/commands/new/templates.js.map +1 -0
  129. package/dist/commands/open.js +203 -0
  130. package/dist/commands/open.js.map +1 -0
  131. package/dist/commands/policy/format.js +152 -0
  132. package/dist/commands/policy/format.js.map +1 -0
  133. package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
  134. package/dist/commands/policy/gate.js.map +1 -0
  135. package/dist/commands/rebase/edit.js +106 -0
  136. package/dist/commands/rebase/edit.js.map +1 -0
  137. package/dist/commands/rebase/living.js +185 -0
  138. package/dist/commands/rebase/living.js.map +1 -0
  139. package/dist/commands/rebase/plan.js +245 -0
  140. package/dist/commands/rebase/plan.js.map +1 -0
  141. package/dist/commands/rebase/rebase.js +275 -0
  142. package/dist/commands/rebase/rebase.js.map +1 -0
  143. package/dist/commands/seed/commit.js +192 -0
  144. package/dist/commands/seed/commit.js.map +1 -0
  145. package/dist/commands/seed/plan.js +133 -0
  146. package/dist/commands/seed/plan.js.map +1 -0
  147. package/dist/commands/seed/seed.js +172 -0
  148. package/dist/commands/seed/seed.js.map +1 -0
  149. package/dist/commands/show/feature.js +178 -0
  150. package/dist/commands/show/feature.js.map +1 -0
  151. package/dist/commands/show/marks.js +24 -0
  152. package/dist/commands/show/marks.js.map +1 -0
  153. package/dist/commands/{show.js → show/service.js} +63 -152
  154. package/dist/commands/show/service.js.map +1 -0
  155. package/dist/commands/show/show.js +77 -0
  156. package/dist/commands/show/show.js.map +1 -0
  157. package/dist/commands/status/json.js +45 -0
  158. package/dist/commands/status/json.js.map +1 -0
  159. package/dist/commands/status/print.js +157 -0
  160. package/dist/commands/status/print.js.map +1 -0
  161. package/dist/commands/status/status.js +119 -0
  162. package/dist/commands/status/status.js.map +1 -0
  163. package/dist/commands/steps/steps.js +189 -0
  164. package/dist/commands/steps/steps.js.map +1 -0
  165. package/dist/commands/subsystem/edit.js +192 -0
  166. package/dist/commands/subsystem/edit.js.map +1 -0
  167. package/dist/commands/subsystem/move.js +265 -0
  168. package/dist/commands/subsystem/move.js.map +1 -0
  169. package/dist/commands/subsystem/query.js +115 -0
  170. package/dist/commands/subsystem/query.js.map +1 -0
  171. package/dist/commands/subsystem/subsystem.js +101 -0
  172. package/dist/commands/subsystem/subsystem.js.map +1 -0
  173. package/dist/commands/subsystem/sync.js +103 -0
  174. package/dist/commands/subsystem/sync.js.map +1 -0
  175. package/dist/commands/subsystem/txn/txn.js +94 -0
  176. package/dist/commands/subsystem/txn/txn.js.map +1 -0
  177. package/dist/commands/subsystem/txn/views.js +70 -0
  178. package/dist/commands/subsystem/txn/views.js.map +1 -0
  179. package/dist/commands/unarchive/manifest.js +160 -0
  180. package/dist/commands/unarchive/manifest.js.map +1 -0
  181. package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
  182. package/dist/commands/unarchive/restore.js.map +1 -0
  183. package/dist/commands/unarchive/unarchive.js +67 -0
  184. package/dist/commands/unarchive/unarchive.js.map +1 -0
  185. package/dist/commands/validate/arch-coverage.js +204 -0
  186. package/dist/commands/validate/arch-coverage.js.map +1 -0
  187. package/dist/commands/validate/checks/fleet-shape.js +296 -0
  188. package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
  189. package/dist/commands/validate/checks/health-deps.js +55 -0
  190. package/dist/commands/validate/checks/health-deps.js.map +1 -0
  191. package/dist/commands/validate/checks/requirements.js +254 -0
  192. package/dist/commands/validate/checks/requirements.js.map +1 -0
  193. package/dist/commands/validate/checks/sources.js +76 -0
  194. package/dist/commands/validate/checks/sources.js.map +1 -0
  195. package/dist/commands/validate/checks/vocabulary.js +31 -0
  196. package/dist/commands/validate/checks/vocabulary.js.map +1 -0
  197. package/dist/commands/validate/feature.js +216 -0
  198. package/dist/commands/validate/feature.js.map +1 -0
  199. package/dist/commands/validate/fleet/census.js +69 -0
  200. package/dist/commands/validate/fleet/census.js.map +1 -0
  201. package/dist/commands/validate/fleet/kind-tags.js +39 -0
  202. package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
  203. package/dist/commands/validate/fleet/landscape.js +353 -0
  204. package/dist/commands/validate/fleet/landscape.js.map +1 -0
  205. package/dist/commands/validate/fleet/load.js +52 -0
  206. package/dist/commands/validate/fleet/load.js.map +1 -0
  207. package/dist/commands/validate/fleet/obligations.js +270 -0
  208. package/dist/commands/validate/fleet/obligations.js.map +1 -0
  209. package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
  210. package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
  211. package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
  212. package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
  213. package/dist/commands/validate/fleet/scorecard/print.js +63 -0
  214. package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
  215. package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
  216. package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
  217. package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
  218. package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
  219. package/dist/commands/validate/fleet/usecases/place.js +13 -0
  220. package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
  221. package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
  222. package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
  223. package/dist/commands/validate/fleet/usecases/steps.js +197 -0
  224. package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
  225. package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
  226. package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
  227. package/dist/commands/validate/fleet/views/ids.js +40 -0
  228. package/dist/commands/validate/fleet/views/ids.js.map +1 -0
  229. package/dist/commands/validate/fleet/views/stale.js +59 -0
  230. package/dist/commands/validate/fleet/views/stale.js.map +1 -0
  231. package/dist/commands/validate/links/corpus.js +92 -0
  232. package/dist/commands/validate/links/corpus.js.map +1 -0
  233. package/dist/commands/validate/links/glossary.js +75 -0
  234. package/dist/commands/validate/links/glossary.js.map +1 -0
  235. package/dist/commands/validate/report.js +269 -0
  236. package/dist/commands/validate/report.js.map +1 -0
  237. package/dist/commands/validate/service/api.js +338 -0
  238. package/dist/commands/validate/service/api.js.map +1 -0
  239. package/dist/commands/validate/service/events/events.js +219 -0
  240. package/dist/commands/validate/service/events/events.js.map +1 -0
  241. package/dist/commands/validate/service/events/message-producers.js +77 -0
  242. package/dist/commands/validate/service/events/message-producers.js.map +1 -0
  243. package/dist/commands/validate/service/evidence-pins.js +131 -0
  244. package/dist/commands/validate/service/evidence-pins.js.map +1 -0
  245. package/dist/commands/validate/service/service.js +251 -0
  246. package/dist/commands/validate/service/service.js.map +1 -0
  247. package/dist/commands/validate/service/specs.js +251 -0
  248. package/dist/commands/validate/service/specs.js.map +1 -0
  249. package/dist/commands/validate/service/spine.js +113 -0
  250. package/dist/commands/validate/service/spine.js.map +1 -0
  251. package/dist/commands/validate/validate.js +338 -0
  252. package/dist/commands/validate/validate.js.map +1 -0
  253. package/dist/commands/verify/cross/diff.js +182 -0
  254. package/dist/commands/verify/cross/diff.js.map +1 -0
  255. package/dist/commands/verify/evidence/contract.js +32 -0
  256. package/dist/commands/verify/evidence/contract.js.map +1 -0
  257. package/dist/commands/verify/evidence/pins.js +52 -0
  258. package/dist/commands/verify/evidence/pins.js.map +1 -0
  259. package/dist/commands/verify/evidence/read.js +72 -0
  260. package/dist/commands/verify/evidence/read.js.map +1 -0
  261. package/dist/commands/verify/frozen.js +241 -0
  262. package/dist/commands/verify/frozen.js.map +1 -0
  263. package/dist/commands/verify/record.js +226 -0
  264. package/dist/commands/verify/record.js.map +1 -0
  265. package/dist/commands/verify/report.js +223 -0
  266. package/dist/commands/verify/report.js.map +1 -0
  267. package/dist/commands/verify/results.js +246 -0
  268. package/dist/commands/verify/results.js.map +1 -0
  269. package/dist/commands/verify/verify.js +217 -0
  270. package/dist/commands/verify/verify.js.map +1 -0
  271. package/dist/commands/vouch/contract.js +11 -0
  272. package/dist/commands/vouch/contract.js.map +1 -0
  273. package/dist/commands/vouch/pack/contract.js +2 -0
  274. package/dist/commands/vouch/pack/contract.js.map +1 -0
  275. package/dist/commands/vouch/pack/pack.js +266 -0
  276. package/dist/commands/vouch/pack/pack.js.map +1 -0
  277. package/dist/commands/vouch/pack/print.js +271 -0
  278. package/dist/commands/vouch/pack/print.js.map +1 -0
  279. package/dist/commands/vouch/pack/sections.js +142 -0
  280. package/dist/commands/vouch/pack/sections.js.map +1 -0
  281. package/dist/commands/vouch/prompt/confirm.js +46 -0
  282. package/dist/commands/vouch/prompt/confirm.js.map +1 -0
  283. package/dist/commands/vouch/report.js +101 -0
  284. package/dist/commands/vouch/report.js.map +1 -0
  285. package/dist/commands/vouch/run.js +285 -0
  286. package/dist/commands/vouch/run.js.map +1 -0
  287. package/dist/commands/vouch/sample/plan.js +125 -0
  288. package/dist/commands/vouch/sample/plan.js.map +1 -0
  289. package/dist/commands/vouch/sample/print.js +109 -0
  290. package/dist/commands/vouch/sample/print.js.map +1 -0
  291. package/dist/commands/vouch/vet/verify.js +123 -0
  292. package/dist/commands/vouch/vet/verify.js.map +1 -0
  293. package/dist/commands/vouch/vouch.js +176 -0
  294. package/dist/commands/vouch/vouch.js.map +1 -0
  295. package/dist/core/agent/agents-md/artifacts.js +220 -0
  296. package/dist/core/agent/agents-md/artifacts.js.map +1 -0
  297. package/dist/core/agent/agents-md/command-map.js +324 -0
  298. package/dist/core/agent/agents-md/command-map.js.map +1 -0
  299. package/dist/core/agent/agents-md/cycle.js +165 -0
  300. package/dist/core/agent/agents-md/cycle.js.map +1 -0
  301. package/dist/core/agent/agents-md/map/explain.js +34 -0
  302. package/dist/core/agent/agents-md/map/explain.js.map +1 -0
  303. package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
  304. package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
  305. package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
  306. package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
  307. package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
  308. package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
  309. package/dist/core/agent/agents-md/map/mcp.js +41 -0
  310. package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
  311. package/dist/core/agent/agents-md/map/status.js +62 -0
  312. package/dist/core/agent/agents-md/map/status.js.map +1 -0
  313. package/dist/core/agent/agents-md/map/subsystem.js +32 -0
  314. package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
  315. package/dist/core/agent/agents-md/map/usecases.js +60 -0
  316. package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
  317. package/dist/core/agent/agents-md/refusals.js +246 -0
  318. package/dist/core/agent/agents-md/refusals.js.map +1 -0
  319. package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
  320. package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
  321. package/dist/core/agent/agents-md/spine.js +40 -0
  322. package/dist/core/agent/agents-md/spine.js.map +1 -0
  323. package/dist/core/agent/agents-md.js +170 -0
  324. package/dist/core/agent/agents-md.js.map +1 -0
  325. package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
  326. package/dist/core/agent/agents-stamp.js.map +1 -0
  327. package/dist/core/agent/contract.js +2 -0
  328. package/dist/core/agent/contract.js.map +1 -0
  329. package/dist/core/agent/protocol.js +193 -0
  330. package/dist/core/agent/protocol.js.map +1 -0
  331. package/dist/core/agent/scaffold.js +259 -0
  332. package/dist/core/agent/scaffold.js.map +1 -0
  333. package/dist/core/agent/support/report.js +103 -0
  334. package/dist/core/agent/support/report.js.map +1 -0
  335. package/dist/core/agent/tools/dialects.js +150 -0
  336. package/dist/core/agent/tools/dialects.js.map +1 -0
  337. package/dist/core/agent/tools/registry.js +228 -0
  338. package/dist/core/agent/tools/registry.js.map +1 -0
  339. package/dist/core/agent/workflows/adopt.js +133 -0
  340. package/dist/core/agent/workflows/adopt.js.map +1 -0
  341. package/dist/core/agent/workflows/check.js +337 -0
  342. package/dist/core/agent/workflows/check.js.map +1 -0
  343. package/dist/core/agent/workflows/closing.js +262 -0
  344. package/dist/core/agent/workflows/closing.js.map +1 -0
  345. package/dist/core/agent/workflows/feature.js +105 -0
  346. package/dist/core/agent/workflows/feature.js.map +1 -0
  347. package/dist/core/agent/workflows/implement.js +105 -0
  348. package/dist/core/agent/workflows/implement.js.map +1 -0
  349. package/dist/core/agent/workflows/reference/authoring.js +209 -0
  350. package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
  351. package/dist/core/agent/workflows/reference/codes.js +65 -0
  352. package/dist/core/agent/workflows/reference/codes.js.map +1 -0
  353. package/dist/core/agent/workflows/reference/done-check.js +188 -0
  354. package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
  355. package/dist/core/agent/workflows/reference/reference.js +16 -0
  356. package/dist/core/agent/workflows/reference/reference.js.map +1 -0
  357. package/dist/core/agent/workflows/reference/spine.js +297 -0
  358. package/dist/core/agent/workflows/reference/spine.js.map +1 -0
  359. package/dist/core/asyncapi/baseline/plan.js +110 -0
  360. package/dist/core/asyncapi/baseline/plan.js.map +1 -0
  361. package/dist/core/asyncapi/baseline/surfaces.js +54 -0
  362. package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
  363. package/dist/core/asyncapi/depth.js +164 -0
  364. package/dist/core/asyncapi/depth.js.map +1 -0
  365. package/dist/core/asyncapi/digest.js +141 -0
  366. package/dist/core/asyncapi/digest.js.map +1 -0
  367. package/dist/core/asyncapi/merge/components.js +91 -0
  368. package/dist/core/asyncapi/merge/components.js.map +1 -0
  369. package/dist/core/asyncapi/merge/error.js +33 -0
  370. package/dist/core/asyncapi/merge/error.js.map +1 -0
  371. package/dist/core/asyncapi/merge/markers.js +308 -0
  372. package/dist/core/asyncapi/merge/markers.js.map +1 -0
  373. package/dist/core/asyncapi/merge/merge.js +215 -0
  374. package/dist/core/asyncapi/merge/merge.js.map +1 -0
  375. package/dist/core/asyncapi/merge/pin.js +107 -0
  376. package/dist/core/asyncapi/merge/pin.js.map +1 -0
  377. package/dist/core/asyncapi/model.js +11 -0
  378. package/dist/core/asyncapi/model.js.map +1 -0
  379. package/dist/core/asyncapi/producers.js +50 -0
  380. package/dist/core/asyncapi/producers.js.map +1 -0
  381. package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
  382. package/dist/core/asyncapi/read.js.map +1 -0
  383. package/dist/core/brief/brief.js +225 -0
  384. package/dist/core/brief/brief.js.map +1 -0
  385. package/dist/core/brief/checks.js +250 -0
  386. package/dist/core/brief/checks.js.map +1 -0
  387. package/dist/core/brief/landscape.js +194 -0
  388. package/dist/core/brief/landscape.js.map +1 -0
  389. package/dist/core/brief/targets.js +226 -0
  390. package/dist/core/brief/targets.js.map +1 -0
  391. package/dist/core/brief/unchecked.js +48 -0
  392. package/dist/core/brief/unchecked.js.map +1 -0
  393. package/dist/core/c4/arch.js +174 -0
  394. package/dist/core/c4/arch.js.map +1 -0
  395. package/dist/core/c4/likec4.js +90 -0
  396. package/dist/core/c4/likec4.js.map +1 -0
  397. package/dist/core/c4/parsed/deployment.js +158 -0
  398. package/dist/core/c4/parsed/deployment.js.map +1 -0
  399. package/dist/core/c4/parsed/dynamic-views.js +132 -0
  400. package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
  401. package/dist/core/c4/parsed/specification.js +78 -0
  402. package/dist/core/c4/parsed/specification.js.map +1 -0
  403. package/dist/core/c4/parsed/values.js +57 -0
  404. package/dist/core/c4/parsed/values.js.map +1 -0
  405. package/dist/core/c4/parsed/view-ids.js +75 -0
  406. package/dist/core/c4/parsed/view-ids.js.map +1 -0
  407. package/dist/core/c4/project/architecture.js +45 -0
  408. package/dist/core/c4/project/architecture.js.map +1 -0
  409. package/dist/core/c4/project/documents.js +58 -0
  410. package/dist/core/c4/project/documents.js.map +1 -0
  411. package/dist/core/c4/project/load.js +187 -0
  412. package/dist/core/c4/project/load.js.map +1 -0
  413. package/dist/core/c4/project/staged.js +220 -0
  414. package/dist/core/c4/project/staged.js.map +1 -0
  415. package/dist/core/c4/resolve/service.js +159 -0
  416. package/dist/core/c4/resolve/service.js.map +1 -0
  417. package/dist/core/c4/resolve/steps.js +144 -0
  418. package/dist/core/c4/resolve/steps.js.map +1 -0
  419. package/dist/core/c4/seed/fleet-file.js +201 -0
  420. package/dist/core/c4/seed/fleet-file.js.map +1 -0
  421. package/dist/core/c4/seed/items.js +91 -0
  422. package/dist/core/c4/seed/items.js.map +1 -0
  423. package/dist/core/c4/seed/stamp.js +48 -0
  424. package/dist/core/c4/seed/stamp.js.map +1 -0
  425. package/dist/core/c4/seed/template.js +109 -0
  426. package/dist/core/c4/seed/template.js.map +1 -0
  427. package/dist/core/c4/source-mask.js +82 -0
  428. package/dist/core/c4/source-mask.js.map +1 -0
  429. package/dist/core/c4/source-scan.js +227 -0
  430. package/dist/core/c4/source-scan.js.map +1 -0
  431. package/dist/core/c4/splice/authored-source.js +101 -0
  432. package/dist/core/c4/splice/authored-source.js.map +1 -0
  433. package/dist/core/c4/splice/contract.js +10 -0
  434. package/dist/core/c4/splice/contract.js.map +1 -0
  435. package/dist/core/c4/splice/delta-blocks.js +139 -0
  436. package/dist/core/c4/splice/delta-blocks.js.map +1 -0
  437. package/dist/core/c4/splice/identity/edges.js +36 -0
  438. package/dist/core/c4/splice/identity/edges.js.map +1 -0
  439. package/dist/core/c4/splice/landscape-merge.js +269 -0
  440. package/dist/core/c4/splice/landscape-merge.js.map +1 -0
  441. package/dist/core/c4/splice/placement.js +194 -0
  442. package/dist/core/c4/splice/placement.js.map +1 -0
  443. package/dist/core/c4/workspace.js +174 -0
  444. package/dist/core/c4/workspace.js.map +1 -0
  445. package/dist/core/capabilities/authoring/notes.js +141 -0
  446. package/dist/core/capabilities/authoring/notes.js.map +1 -0
  447. package/dist/core/capabilities/capabilities.js +124 -0
  448. package/dist/core/capabilities/capabilities.js.map +1 -0
  449. package/dist/core/capabilities/delta/doc.js +50 -0
  450. package/dist/core/capabilities/delta/doc.js.map +1 -0
  451. package/dist/core/capabilities/delta/overlay.js +129 -0
  452. package/dist/core/capabilities/delta/overlay.js.map +1 -0
  453. package/dist/core/capabilities/delta/summary.js +57 -0
  454. package/dist/core/capabilities/delta/summary.js.map +1 -0
  455. package/dist/core/capabilities/delta/tree.js +43 -0
  456. package/dist/core/capabilities/delta/tree.js.map +1 -0
  457. package/dist/core/capabilities/delta/uncovered.js +222 -0
  458. package/dist/core/capabilities/delta/uncovered.js.map +1 -0
  459. package/dist/core/capabilities/findings.js +342 -0
  460. package/dist/core/capabilities/findings.js.map +1 -0
  461. package/dist/core/capabilities/realizes/findings.js +181 -0
  462. package/dist/core/capabilities/realizes/findings.js.map +1 -0
  463. package/dist/core/capabilities/realizes/join.js +143 -0
  464. package/dist/core/capabilities/realizes/join.js.map +1 -0
  465. package/dist/core/capabilities/rollup.js +199 -0
  466. package/dist/core/capabilities/rollup.js.map +1 -0
  467. package/dist/core/capabilities/tree.js +126 -0
  468. package/dist/core/capabilities/tree.js.map +1 -0
  469. package/dist/core/capabilities/usecase-join.js +277 -0
  470. package/dist/core/capabilities/usecase-join.js.map +1 -0
  471. package/dist/core/coherence/authoring/scaffold.js +177 -0
  472. package/dist/core/coherence/authoring/scaffold.js.map +1 -0
  473. package/dist/core/coherence/authoring/sentinels.js +74 -0
  474. package/dist/core/coherence/authoring/sentinels.js.map +1 -0
  475. package/dist/core/coherence/coherence.js +287 -0
  476. package/dist/core/coherence/coherence.js.map +1 -0
  477. package/dist/core/coherence/declared.js +269 -0
  478. package/dist/core/coherence/declared.js.map +1 -0
  479. package/dist/core/coherence/events/declared.js +206 -0
  480. package/dist/core/coherence/events/declared.js.map +1 -0
  481. package/dist/core/coherence/events/events.js +204 -0
  482. package/dist/core/coherence/events/events.js.map +1 -0
  483. package/dist/core/coherence/events/grades.js +166 -0
  484. package/dist/core/coherence/events/grades.js.map +1 -0
  485. package/dist/core/coherence/events/lookups.js +122 -0
  486. package/dist/core/coherence/events/lookups.js.map +1 -0
  487. package/dist/core/coherence/events/merged.js +40 -0
  488. package/dist/core/coherence/events/merged.js.map +1 -0
  489. package/dist/core/coherence/living.js +198 -0
  490. package/dist/core/coherence/living.js.map +1 -0
  491. package/dist/core/coherence/lookups.js +155 -0
  492. package/dist/core/coherence/lookups.js.map +1 -0
  493. package/dist/core/coherence/pending.js +39 -0
  494. package/dist/core/coherence/pending.js.map +1 -0
  495. package/dist/core/conflict-markers.js +60 -0
  496. package/dist/core/conflict-markers.js.map +1 -0
  497. package/dist/core/delta/claims.js +73 -0
  498. package/dist/core/delta/claims.js.map +1 -0
  499. package/dist/core/delta/delta.js +296 -0
  500. package/dist/core/delta/delta.js.map +1 -0
  501. package/dist/core/delta/document.js +177 -0
  502. package/dist/core/delta/document.js.map +1 -0
  503. package/dist/core/delta/scope.js +48 -0
  504. package/dist/core/delta/scope.js.map +1 -0
  505. package/dist/core/delta/select.js +237 -0
  506. package/dist/core/delta/select.js.map +1 -0
  507. package/dist/core/dependencies/dependencies.js +141 -0
  508. package/dist/core/dependencies/dependencies.js.map +1 -0
  509. package/dist/core/dependencies/facts.js +165 -0
  510. package/dist/core/dependencies/facts.js.map +1 -0
  511. package/dist/core/dependencies/fanin.js +88 -0
  512. package/dist/core/dependencies/fanin.js.map +1 -0
  513. package/dist/core/dependencies/graph.js +106 -0
  514. package/dist/core/dependencies/graph.js.map +1 -0
  515. package/dist/core/deployment/delta.js +188 -0
  516. package/dist/core/deployment/delta.js.map +1 -0
  517. package/dist/core/deployment/objects.js +145 -0
  518. package/dist/core/deployment/objects.js.map +1 -0
  519. package/dist/core/diff/base-git.js +148 -0
  520. package/dist/core/diff/base-git.js.map +1 -0
  521. package/dist/core/diff/base-state.js +209 -0
  522. package/dist/core/diff/base-state.js.map +1 -0
  523. package/dist/core/diff/current-state.js +96 -0
  524. package/dist/core/diff/current-state.js.map +1 -0
  525. package/dist/core/diff/scope/changed-paths.js +188 -0
  526. package/dist/core/diff/scope/changed-paths.js.map +1 -0
  527. package/dist/core/diff/semantic.js +243 -0
  528. package/dist/core/diff/semantic.js.map +1 -0
  529. package/dist/core/diff/victims.js +247 -0
  530. package/dist/core/diff/victims.js.map +1 -0
  531. package/dist/core/docs.js +7 -64
  532. package/dist/core/docs.js.map +1 -1
  533. package/dist/core/doctor/agents.js +222 -0
  534. package/dist/core/doctor/agents.js.map +1 -0
  535. package/dist/core/doctor/config.js +135 -0
  536. package/dist/core/doctor/config.js.map +1 -0
  537. package/dist/core/doctor/doctor.js +275 -0
  538. package/dist/core/doctor/doctor.js.map +1 -0
  539. package/dist/core/doctor/report.js +13 -0
  540. package/dist/core/doctor/report.js.map +1 -0
  541. package/dist/core/doctor/residue.js +110 -0
  542. package/dist/core/doctor/residue.js.map +1 -0
  543. package/dist/core/document/apply.js +77 -0
  544. package/dist/core/document/apply.js.map +1 -0
  545. package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
  546. package/dist/core/document/frontmatter.js.map +1 -0
  547. package/dist/core/document/grammar/directives.js +143 -0
  548. package/dist/core/document/grammar/directives.js.map +1 -0
  549. package/dist/core/document/parse.js +263 -0
  550. package/dist/core/document/parse.js.map +1 -0
  551. package/dist/core/document/scenarios.js +221 -0
  552. package/dist/core/document/scenarios.js.map +1 -0
  553. package/dist/core/document/spec.js +241 -0
  554. package/dist/core/document/spec.js.map +1 -0
  555. package/dist/core/{config.js → envelope/config.js} +79 -18
  556. package/dist/core/envelope/config.js.map +1 -0
  557. package/dist/core/envelope/json.js +175 -0
  558. package/dist/core/envelope/json.js.map +1 -0
  559. package/dist/core/{version.js → envelope/version.js} +3 -3
  560. package/dist/core/envelope/version.js.map +1 -0
  561. package/dist/core/explain/families.js +175 -0
  562. package/dist/core/explain/families.js.map +1 -0
  563. package/dist/core/explain/fix-tables.js +178 -0
  564. package/dist/core/explain/fix-tables.js.map +1 -0
  565. package/dist/core/explain/lookup.js +181 -0
  566. package/dist/core/explain/lookup.js.map +1 -0
  567. package/dist/core/explain/refusals.js +49 -0
  568. package/dist/core/explain/refusals.js.map +1 -0
  569. package/dist/core/explain/terms.js +144 -0
  570. package/dist/core/explain/terms.js.map +1 -0
  571. package/dist/core/explore/describe.js +105 -0
  572. package/dist/core/explore/describe.js.map +1 -0
  573. package/dist/core/{explore.js → explore/explore.js} +50 -101
  574. package/dist/core/explore/explore.js.map +1 -0
  575. package/dist/core/fleet-context.js +120 -78
  576. package/dist/core/fleet-context.js.map +1 -1
  577. package/dist/core/gate/checks.js +201 -0
  578. package/dist/core/gate/checks.js.map +1 -0
  579. package/dist/core/gate/partners.js +205 -0
  580. package/dist/core/gate/partners.js.map +1 -0
  581. package/dist/core/gate/report.js +47 -0
  582. package/dist/core/gate/report.js.map +1 -0
  583. package/dist/core/gate/verification.js +113 -0
  584. package/dist/core/gate/verification.js.map +1 -0
  585. package/dist/core/gherkin/digest.js +60 -0
  586. package/dist/core/gherkin/digest.js.map +1 -0
  587. package/dist/core/gherkin/emit.js +177 -0
  588. package/dist/core/gherkin/emit.js.map +1 -0
  589. package/dist/core/gherkin/read.js +76 -0
  590. package/dist/core/gherkin/read.js.map +1 -0
  591. package/dist/core/gherkin/stale.js +234 -0
  592. package/dist/core/gherkin/stale.js.map +1 -0
  593. package/dist/core/gherkin/stamp.js +77 -0
  594. package/dist/core/gherkin/stamp.js.map +1 -0
  595. package/dist/core/gherkin/steps/catalogue.js +154 -0
  596. package/dist/core/gherkin/steps/catalogue.js.map +1 -0
  597. package/dist/core/gherkin/steps/inventory.js +98 -0
  598. package/dist/core/gherkin/steps/inventory.js.map +1 -0
  599. package/dist/core/gherkin/steps/phrase.js +72 -0
  600. package/dist/core/gherkin/steps/phrase.js.map +1 -0
  601. package/dist/core/glossary/delta.js +106 -0
  602. package/dist/core/glossary/delta.js.map +1 -0
  603. package/dist/core/glossary/tree.js +86 -0
  604. package/dist/core/glossary/tree.js.map +1 -0
  605. package/dist/core/kernel/concurrency.js.map +1 -0
  606. package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
  607. package/dist/core/kernel/document-bytes.js.map +1 -0
  608. package/dist/core/kernel/fences.js +42 -0
  609. package/dist/core/kernel/fences.js.map +1 -0
  610. package/dist/core/kernel/ids/capability.js +72 -0
  611. package/dist/core/kernel/ids/capability.js.map +1 -0
  612. package/dist/core/kernel/ids/dirs.js +33 -0
  613. package/dist/core/kernel/ids/dirs.js.map +1 -0
  614. package/dist/core/kernel/ids/feature.js +75 -0
  615. package/dist/core/kernel/ids/feature.js.map +1 -0
  616. package/dist/core/kernel/ids/service.js +156 -0
  617. package/dist/core/kernel/ids/service.js.map +1 -0
  618. package/dist/core/kernel/ids/subsystem.js +63 -0
  619. package/dist/core/kernel/ids/subsystem.js.map +1 -0
  620. package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
  621. package/dist/core/kernel/path-safety.js.map +1 -0
  622. package/dist/core/kernel/records.js.map +1 -0
  623. package/dist/core/links/backlinks.js +68 -0
  624. package/dist/core/links/backlinks.js.map +1 -0
  625. package/dist/core/links/case.js +74 -0
  626. package/dist/core/links/case.js.map +1 -0
  627. package/dist/core/links/corpus.js +104 -0
  628. package/dist/core/links/corpus.js.map +1 -0
  629. package/dist/core/links/findings.js +110 -0
  630. package/dist/core/links/findings.js.map +1 -0
  631. package/dist/core/links/parse.js +236 -0
  632. package/dist/core/links/parse.js.map +1 -0
  633. package/dist/core/mcp/argv.js +111 -0
  634. package/dist/core/mcp/argv.js.map +1 -0
  635. package/dist/core/mcp/author-tools.js +72 -0
  636. package/dist/core/mcp/author-tools.js.map +1 -0
  637. package/dist/core/mcp/framing.js +46 -0
  638. package/dist/core/mcp/framing.js.map +1 -0
  639. package/dist/core/mcp/protocol.js +280 -0
  640. package/dist/core/mcp/protocol.js.map +1 -0
  641. package/dist/core/mcp/tools.js +314 -0
  642. package/dist/core/mcp/tools.js.map +1 -0
  643. package/dist/core/obligations/obligations.js +106 -0
  644. package/dist/core/obligations/obligations.js.map +1 -0
  645. package/dist/core/openapi/baseline/gate.js +166 -0
  646. package/dist/core/openapi/baseline/gate.js.map +1 -0
  647. package/dist/core/openapi/baseline/plan.js +112 -0
  648. package/dist/core/openapi/baseline/plan.js.map +1 -0
  649. package/dist/core/openapi/baseline/record.js +175 -0
  650. package/dist/core/openapi/baseline/record.js.map +1 -0
  651. package/dist/core/openapi/depth.js +122 -0
  652. package/dist/core/openapi/depth.js.map +1 -0
  653. package/dist/core/openapi/digest.js +189 -0
  654. package/dist/core/openapi/digest.js.map +1 -0
  655. package/dist/core/{openapi.js → openapi/doc.js} +44 -106
  656. package/dist/core/openapi/doc.js.map +1 -0
  657. package/dist/core/openapi/merge/components.js +234 -0
  658. package/dist/core/openapi/merge/components.js.map +1 -0
  659. package/dist/core/openapi/merge/error.js +26 -0
  660. package/dist/core/openapi/merge/error.js.map +1 -0
  661. package/dist/core/openapi/merge/markers.js +208 -0
  662. package/dist/core/openapi/merge/markers.js.map +1 -0
  663. package/dist/core/openapi/merge/merge.js +256 -0
  664. package/dist/core/openapi/merge/merge.js.map +1 -0
  665. package/dist/core/openapi/merge/pin.js +128 -0
  666. package/dist/core/openapi/merge/pin.js.map +1 -0
  667. package/dist/core/openspec/decide/capabilities.js +244 -0
  668. package/dist/core/openspec/decide/capabilities.js.map +1 -0
  669. package/dist/core/openspec/decide/dispositions.js +63 -0
  670. package/dist/core/openspec/decide/dispositions.js.map +1 -0
  671. package/dist/core/openspec/decide/renames.js +179 -0
  672. package/dist/core/openspec/decide/renames.js.map +1 -0
  673. package/dist/core/openspec/decide/verdict.js +84 -0
  674. package/dist/core/openspec/decide/verdict.js.map +1 -0
  675. package/dist/core/openspec/inventory.js +221 -0
  676. package/dist/core/openspec/inventory.js.map +1 -0
  677. package/dist/core/openspec/model/mapping.js +76 -0
  678. package/dist/core/openspec/model/mapping.js.map +1 -0
  679. package/dist/core/openspec/model/model.js +65 -0
  680. package/dist/core/openspec/model/model.js.map +1 -0
  681. package/dist/core/openspec/scan/changes.js +235 -0
  682. package/dist/core/openspec/scan/changes.js.map +1 -0
  683. package/dist/core/openspec/scan/config.js +200 -0
  684. package/dist/core/openspec/scan/config.js.map +1 -0
  685. package/dist/core/openspec/scan/shape.js +260 -0
  686. package/dist/core/openspec/scan/shape.js.map +1 -0
  687. package/dist/core/openspec/scan/walk.js +133 -0
  688. package/dist/core/openspec/scan/walk.js.map +1 -0
  689. package/dist/core/openspec/scan/workspace.js +71 -0
  690. package/dist/core/openspec/scan/workspace.js.map +1 -0
  691. package/dist/core/owners/codeowners.js +141 -0
  692. package/dist/core/owners/codeowners.js.map +1 -0
  693. package/dist/core/pack/features.js +120 -0
  694. package/dist/core/pack/features.js.map +1 -0
  695. package/dist/core/pack/joins.js +112 -0
  696. package/dist/core/pack/joins.js.map +1 -0
  697. package/dist/core/pack/living.js +137 -0
  698. package/dist/core/pack/living.js.map +1 -0
  699. package/dist/core/pack/pack.js +76 -0
  700. package/dist/core/pack/pack.js.map +1 -0
  701. package/dist/core/permissions/permissions.js +87 -0
  702. package/dist/core/permissions/permissions.js.map +1 -0
  703. package/dist/core/projection/api.js +84 -0
  704. package/dist/core/projection/api.js.map +1 -0
  705. package/dist/core/projection/arch-slice.js +63 -0
  706. package/dist/core/projection/arch-slice.js.map +1 -0
  707. package/dist/core/projection/events.js +34 -0
  708. package/dist/core/projection/events.js.map +1 -0
  709. package/dist/core/provenance/findings.js +208 -0
  710. package/dist/core/provenance/findings.js.map +1 -0
  711. package/dist/core/provenance/git.js +225 -0
  712. package/dist/core/provenance/git.js.map +1 -0
  713. package/dist/core/provenance/gitq/moves.js +182 -0
  714. package/dist/core/provenance/gitq/moves.js.map +1 -0
  715. package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
  716. package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
  717. package/dist/core/provenance/sample/scope.js +238 -0
  718. package/dist/core/provenance/sample/scope.js.map +1 -0
  719. package/dist/core/provenance/sample/sections.js +61 -0
  720. package/dist/core/provenance/sample/sections.js.map +1 -0
  721. package/dist/core/provenance/sources.js +263 -0
  722. package/dist/core/provenance/sources.js.map +1 -0
  723. package/dist/core/provenance/stamp.js +232 -0
  724. package/dist/core/provenance/stamp.js.map +1 -0
  725. package/dist/core/provenance/walk.js +157 -0
  726. package/dist/core/provenance/walk.js.map +1 -0
  727. package/dist/core/repo/authored/paths.js +210 -0
  728. package/dist/core/repo/authored/paths.js.map +1 -0
  729. package/dist/core/repo/entries.js +126 -0
  730. package/dist/core/repo/entries.js.map +1 -0
  731. package/dist/core/repo/paths.js +277 -0
  732. package/dist/core/repo/paths.js.map +1 -0
  733. package/dist/core/repo/repo.js +248 -0
  734. package/dist/core/repo/repo.js.map +1 -0
  735. package/dist/core/repo/service-target.js +153 -0
  736. package/dist/core/repo/service-target.js.map +1 -0
  737. package/dist/core/repo/state.js +58 -0
  738. package/dist/core/repo/state.js.map +1 -0
  739. package/dist/core/repo/tree/find.js +52 -0
  740. package/dist/core/repo/tree/find.js.map +1 -0
  741. package/dist/core/repo/tree/fs.js +64 -0
  742. package/dist/core/repo/tree/fs.js.map +1 -0
  743. package/dist/core/repo/tree/marker.js +61 -0
  744. package/dist/core/repo/tree/marker.js.map +1 -0
  745. package/dist/core/repo/tree/views.js +172 -0
  746. package/dist/core/repo/tree/views.js.map +1 -0
  747. package/dist/core/repo/tree/walk.js +234 -0
  748. package/dist/core/repo/tree/walk.js.map +1 -0
  749. package/dist/core/results.js +99 -2
  750. package/dist/core/results.js.map +1 -1
  751. package/dist/core/scaffold/landscape.js +131 -0
  752. package/dist/core/scaffold/landscape.js.map +1 -0
  753. package/dist/core/scaffold/readme.js +84 -0
  754. package/dist/core/scaffold/readme.js.map +1 -0
  755. package/dist/core/staging/commit.js +241 -0
  756. package/dist/core/staging/commit.js.map +1 -0
  757. package/dist/core/staging/interrupted.js +96 -0
  758. package/dist/core/staging/interrupted.js.map +1 -0
  759. package/dist/core/staging/lock.js +206 -0
  760. package/dist/core/staging/lock.js.map +1 -0
  761. package/dist/core/staging/recovery/finding.js +35 -0
  762. package/dist/core/staging/recovery/finding.js.map +1 -0
  763. package/dist/core/staging/recovery/intent.js +98 -0
  764. package/dist/core/staging/recovery/intent.js.map +1 -0
  765. package/dist/core/staging/recovery/recover.js +126 -0
  766. package/dist/core/staging/recovery/recover.js.map +1 -0
  767. package/dist/core/staging/recovery/residue.js +62 -0
  768. package/dist/core/staging/recovery/residue.js.map +1 -0
  769. package/dist/core/staging/snapshot.js +202 -0
  770. package/dist/core/staging/snapshot.js.map +1 -0
  771. package/dist/core/staging/txn/forward.js +171 -0
  772. package/dist/core/staging/txn/forward.js.map +1 -0
  773. package/dist/core/staging/txn/journal.js +139 -0
  774. package/dist/core/staging/txn/journal.js.map +1 -0
  775. package/dist/core/staging/txn/transaction.js +75 -0
  776. package/dist/core/staging/txn/transaction.js.map +1 -0
  777. package/dist/core/staging/writes.js +113 -0
  778. package/dist/core/staging/writes.js.map +1 -0
  779. package/dist/core/status/actions/execution.js +49 -0
  780. package/dist/core/status/actions/execution.js.map +1 -0
  781. package/dist/core/status/contracts.js +102 -0
  782. package/dist/core/status/contracts.js.map +1 -0
  783. package/dist/core/status/feature/artifacts.js +196 -0
  784. package/dist/core/status/feature/artifacts.js.map +1 -0
  785. package/dist/core/status/feature/feature.js +207 -0
  786. package/dist/core/status/feature/feature.js.map +1 -0
  787. package/dist/core/status/feature/next.js +150 -0
  788. package/dist/core/status/feature/next.js.map +1 -0
  789. package/dist/core/status/feature/state.js +21 -0
  790. package/dist/core/status/feature/state.js.map +1 -0
  791. package/dist/core/status/fleet/fleet.js +197 -0
  792. package/dist/core/status/fleet/fleet.js.map +1 -0
  793. package/dist/core/status/fleet/next.js +207 -0
  794. package/dist/core/status/fleet/next.js.map +1 -0
  795. package/dist/core/status/interrupted.js +97 -0
  796. package/dist/core/status/interrupted.js.map +1 -0
  797. package/dist/core/status/report.js +31 -0
  798. package/dist/core/status/report.js.map +1 -0
  799. package/dist/core/status/scan.js +41 -0
  800. package/dist/core/status/scan.js.map +1 -0
  801. package/dist/core/status/verification.js +201 -0
  802. package/dist/core/status/verification.js.map +1 -0
  803. package/dist/core/usecases/capability.js +169 -0
  804. package/dist/core/usecases/capability.js.map +1 -0
  805. package/dist/core/usecases/delta/claims.js +116 -0
  806. package/dist/core/usecases/delta/claims.js.map +1 -0
  807. package/dist/core/usecases/delta/flows.js +103 -0
  808. package/dist/core/usecases/delta/flows.js.map +1 -0
  809. package/dist/core/usecases/delta/overlay.js +72 -0
  810. package/dist/core/usecases/delta/overlay.js.map +1 -0
  811. package/dist/core/usecases/fleet.js +137 -0
  812. package/dist/core/usecases/fleet.js.map +1 -0
  813. package/dist/core/usecases/operations.js +111 -0
  814. package/dist/core/usecases/operations.js.map +1 -0
  815. package/dist/core/usecases/place.js +44 -0
  816. package/dist/core/usecases/place.js.map +1 -0
  817. package/dist/core/usecases/touch.js +101 -0
  818. package/dist/core/usecases/touch.js.map +1 -0
  819. package/dist/core/verify/answers.js +173 -0
  820. package/dist/core/verify/answers.js.map +1 -0
  821. package/dist/core/verify/build.js +150 -0
  822. package/dist/core/verify/build.js.map +1 -0
  823. package/dist/core/verify/checklist.js +201 -0
  824. package/dist/core/verify/checklist.js.map +1 -0
  825. package/dist/core/verify/claims/identity.js +61 -0
  826. package/dist/core/verify/claims/identity.js.map +1 -0
  827. package/dist/core/verify/cross/diff.js +97 -0
  828. package/dist/core/verify/cross/diff.js.map +1 -0
  829. package/dist/core/verify/evidence/contract.js +169 -0
  830. package/dist/core/verify/evidence/contract.js.map +1 -0
  831. package/dist/core/verify/file.js +257 -0
  832. package/dist/core/verify/file.js.map +1 -0
  833. package/dist/core/verify/pins/lint.js +157 -0
  834. package/dist/core/verify/pins/lint.js.map +1 -0
  835. package/dist/core/verify/pins/pin.js +95 -0
  836. package/dist/core/verify/pins/pin.js.map +1 -0
  837. package/dist/core/verify/record.js +142 -0
  838. package/dist/core/verify/record.js.map +1 -0
  839. package/dist/core/verify/store/commit.js +89 -0
  840. package/dist/core/verify/store/commit.js.map +1 -0
  841. package/dist/core/verify/store/render.js +57 -0
  842. package/dist/core/verify/store/render.js.map +1 -0
  843. package/dist/core/vocabulary/health.js +91 -0
  844. package/dist/core/vocabulary/health.js.map +1 -0
  845. package/dist/core/vocabulary/issue.js +58 -0
  846. package/dist/core/vocabulary/issue.js.map +1 -0
  847. package/dist/core/vocabulary/maturity.js +145 -0
  848. package/dist/core/vocabulary/maturity.js.map +1 -0
  849. package/dist/core/{report.js → vocabulary/report.js} +30 -2
  850. package/dist/core/vocabulary/report.js.map +1 -0
  851. package/dist/core/vocabulary/steps.js +266 -0
  852. package/dist/core/vocabulary/steps.js.map +1 -0
  853. package/dist/core/workspace/discover.js +239 -0
  854. package/dist/core/workspace/discover.js.map +1 -0
  855. package/dist/core/workspace/render.js +52 -0
  856. package/dist/core/workspace/render.js.map +1 -0
  857. package/examples/README.md +189 -0
  858. package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
  859. package/examples/docs/architecture/capabilities.yaml +31 -0
  860. package/examples/docs/architecture/landscape.likec4 +280 -0
  861. package/examples/docs/architecture/obligations.yaml +23 -0
  862. package/examples/docs/architecture/permissions.yaml +50 -0
  863. package/examples/docs/architecture/subsystems.likec4 +11 -0
  864. package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
  865. package/examples/docs/capabilities/checkout/spec.md +55 -0
  866. package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
  867. package/examples/docs/capabilities/order-notifications/spec.md +43 -0
  868. package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
  869. package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
  870. package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
  871. package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
  872. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
  873. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
  874. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
  875. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
  876. package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
  877. package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
  878. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
  879. package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
  880. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
  881. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
  882. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
  883. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
  884. package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
  885. package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
  886. package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
  887. package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
  888. package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
  889. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
  890. package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
  891. package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
  892. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
  893. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
  894. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
  895. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
  896. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
  897. package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
  898. package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
  899. package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
  900. package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
  901. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
  902. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
  903. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
  904. package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
  905. package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
  906. package/examples/docs/glossary/order.md +15 -0
  907. package/examples/docs/glossary/payments/authorization.md +14 -0
  908. package/examples/docs/likec4.config.json +9 -0
  909. package/examples/docs/loam.json +3 -0
  910. package/examples/docs/services/checkout-web/model.likec4 +109 -0
  911. package/examples/docs/services/checkout-web/spec.md +91 -0
  912. package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
  913. package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
  914. package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
  915. package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
  916. package/examples/docs/services/order-service/arch.spec.md +127 -0
  917. package/examples/docs/services/order-service/asyncapi.yaml +108 -0
  918. package/examples/docs/services/order-service/health.yaml +57 -0
  919. package/examples/docs/services/order-service/model.likec4 +138 -0
  920. package/examples/docs/services/order-service/openapi.yaml +257 -0
  921. package/examples/docs/services/order-service/runbook.md +62 -0
  922. package/examples/docs/services/order-service/spec.md +134 -0
  923. package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
  924. package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
  925. package/examples/docs/services/payment-service/arch.spec.md +73 -0
  926. package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
  927. package/examples/docs/services/payment-service/health.yaml +39 -0
  928. package/examples/docs/services/payment-service/model.likec4 +67 -0
  929. package/examples/docs/services/payment-service/openapi.yaml +151 -0
  930. package/examples/docs/services/payment-service/runbook.md +37 -0
  931. package/examples/docs/services/payment-service/spec.md +88 -0
  932. package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
  933. package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
  934. package/examples/docs/services/platform/identity-service/health.yaml +70 -0
  935. package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
  936. package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
  937. package/examples/docs/services/platform/identity-service/runbook.md +95 -0
  938. package/examples/docs/services/platform/identity-service/spec.md +119 -0
  939. package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
  940. package/examples/docs/services/platform/notification-service/health.yaml +35 -0
  941. package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
  942. package/examples/docs/services/platform/notification-service/runbook.md +48 -0
  943. package/examples/docs/services/platform/notification-service/spec.md +112 -0
  944. package/examples/docs/services/platform/subsystem.yaml +2 -0
  945. package/examples/fleet.yaml +57 -0
  946. package/package.json +19 -6
  947. package/dist/commands/adopt.js +0 -240
  948. package/dist/commands/adopt.js.map +0 -1
  949. package/dist/commands/archive.js +0 -1313
  950. package/dist/commands/archive.js.map +0 -1
  951. package/dist/commands/delta.js +0 -388
  952. package/dist/commands/delta.js.map +0 -1
  953. package/dist/commands/docs-repo-gate.js.map +0 -1
  954. package/dist/commands/format.js +0 -17
  955. package/dist/commands/format.js.map +0 -1
  956. package/dist/commands/gherkin.js +0 -309
  957. package/dist/commands/gherkin.js.map +0 -1
  958. package/dist/commands/init.js.map +0 -1
  959. package/dist/commands/list.js +0 -295
  960. package/dist/commands/list.js.map +0 -1
  961. package/dist/commands/migrate-openspec.js +0 -950
  962. package/dist/commands/migrate-openspec.js.map +0 -1
  963. package/dist/commands/new.js +0 -413
  964. package/dist/commands/new.js.map +0 -1
  965. package/dist/commands/rebase.js +0 -356
  966. package/dist/commands/rebase.js.map +0 -1
  967. package/dist/commands/show.js.map +0 -1
  968. package/dist/commands/status.js +0 -262
  969. package/dist/commands/status.js.map +0 -1
  970. package/dist/commands/unarchive.js.map +0 -1
  971. package/dist/commands/validate.js +0 -1773
  972. package/dist/commands/validate.js.map +0 -1
  973. package/dist/commands/verify.js +0 -775
  974. package/dist/commands/verify.js.map +0 -1
  975. package/dist/commands/vouch.js +0 -421
  976. package/dist/commands/vouch.js.map +0 -1
  977. package/dist/core/agent.js +0 -2173
  978. package/dist/core/agent.js.map +0 -1
  979. package/dist/core/agents-stamp.js.map +0 -1
  980. package/dist/core/arch.js +0 -130
  981. package/dist/core/arch.js.map +0 -1
  982. package/dist/core/asyncapi.js.map +0 -1
  983. package/dist/core/brief.js +0 -755
  984. package/dist/core/brief.js.map +0 -1
  985. package/dist/core/coherence.js +0 -577
  986. package/dist/core/coherence.js.map +0 -1
  987. package/dist/core/concurrency.js.map +0 -1
  988. package/dist/core/config.js.map +0 -1
  989. package/dist/core/delta.js +0 -476
  990. package/dist/core/delta.js.map +0 -1
  991. package/dist/core/dependencies.js +0 -356
  992. package/dist/core/dependencies.js.map +0 -1
  993. package/dist/core/doctor.js +0 -596
  994. package/dist/core/doctor.js.map +0 -1
  995. package/dist/core/document-bytes.js.map +0 -1
  996. package/dist/core/explore.js.map +0 -1
  997. package/dist/core/frontmatter.js.map +0 -1
  998. package/dist/core/gherkin.js +0 -463
  999. package/dist/core/gherkin.js.map +0 -1
  1000. package/dist/core/health.js +0 -67
  1001. package/dist/core/health.js.map +0 -1
  1002. package/dist/core/ids.js +0 -122
  1003. package/dist/core/ids.js.map +0 -1
  1004. package/dist/core/issue.js +0 -9
  1005. package/dist/core/issue.js.map +0 -1
  1006. package/dist/core/json.js +0 -74
  1007. package/dist/core/json.js.map +0 -1
  1008. package/dist/core/likec4.js +0 -489
  1009. package/dist/core/likec4.js.map +0 -1
  1010. package/dist/core/maturity.js +0 -66
  1011. package/dist/core/maturity.js.map +0 -1
  1012. package/dist/core/openapi-merge.js +0 -552
  1013. package/dist/core/openapi-merge.js.map +0 -1
  1014. package/dist/core/openapi.js.map +0 -1
  1015. package/dist/core/openspec-inventory.js +0 -1440
  1016. package/dist/core/openspec-inventory.js.map +0 -1
  1017. package/dist/core/path-safety.js.map +0 -1
  1018. package/dist/core/provenance.js +0 -787
  1019. package/dist/core/provenance.js.map +0 -1
  1020. package/dist/core/records.js.map +0 -1
  1021. package/dist/core/repo.js +0 -438
  1022. package/dist/core/repo.js.map +0 -1
  1023. package/dist/core/report.js.map +0 -1
  1024. package/dist/core/spec.js +0 -505
  1025. package/dist/core/spec.js.map +0 -1
  1026. package/dist/core/staging.js +0 -890
  1027. package/dist/core/staging.js.map +0 -1
  1028. package/dist/core/status.js +0 -1132
  1029. package/dist/core/status.js.map +0 -1
  1030. package/dist/core/steps.js +0 -72
  1031. package/dist/core/steps.js.map +0 -1
  1032. package/dist/core/verify.js +0 -807
  1033. package/dist/core/verify.js.map +0 -1
  1034. package/dist/core/version.js.map +0 -1
  1035. /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
  1036. /package/dist/core/{records.js → kernel/records.js} +0 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * This service's requirement documents: spec.md, arch.spec.md, and the
3
+ * obligations the second of them carries that the first never does.
4
+ *
5
+ * Two entry points because the two happen at different moments in a service's
6
+ * grading and the order of findings is part of the report. `readServiceSpecs`
7
+ * runs first — everything downstream joins through the requirements it parses —
8
+ * while `archAxisFindings` runs last, after both spines, because `covers.unknown`
9
+ * resolves against the C4 model and the landscape and cannot be answered until
10
+ * those are in hand.
11
+ *
12
+ * The arch axis is advisory in what it ASKS for (covers.unknown,
13
+ * health.uncovered are warnings; arch.spec.md's absence is not a finding at all,
14
+ * because partial adoption is supported) but not in whether the file is
15
+ * readable: a conflict marker or a stepless scenario is graded exactly as it is
16
+ * on the business axis. One file, one rule.
17
+ */
18
+ import { existsSync } from "node:fs";
19
+ import { readFile } from "node:fs/promises";
20
+ import {} from "../../../core/c4/likec4.js";
21
+ import {} from "../../../core/kernel/ids/service.js";
22
+ import {} from "../../../core/repo/paths.js";
23
+ import {} from "../../../core/vocabulary/report.js";
24
+ import { parseRequirements } from "../../../core/document/parse.js";
25
+ import { assertionlessFindings, examplesFindings, steplessFindings, } from "../../../core/document/scenarios.js";
26
+ import {} from "../../../core/document/spec.js";
27
+ import {} from "../../../core/c4/arch.js";
28
+ import { hasDeployment } from "../../../core/c4/parsed/deployment.js";
29
+ import { loadArchitecture } from "../../../core/c4/project/architecture.js";
30
+ import {} from "../../../core/kernel/ids/dirs.js";
31
+ import { readHealth } from "../../../core/vocabulary/health.js";
32
+ import { documentConflictFinding } from "../../../core/conflict-markers.js";
33
+ import { FleetContext } from "../../../core/fleet-context.js";
34
+ import { healthDependencyFindings } from "../checks/health-deps.js";
35
+ import { coverageFinding, coversEntries, coversUnknownFindings, duplicateRequirementFindings, repeatedListLineFindings, requirementIdFindings, } from "../checks/requirements.js";
36
+ export async function readServiceSpecs(input) {
37
+ const { service, paths, fleet } = input;
38
+ const findings = [];
39
+ // Requirement coverage.
40
+ let reqs = [];
41
+ if (existsSync(paths.spec)) {
42
+ const specText = fleet === undefined ? await readFile(paths.spec, "utf8") : await fleet.readText(paths.spec);
43
+ reqs = fleet === undefined ? parseRequirements(specText) : await fleet.readRequirements(paths.spec);
44
+ // Conflict markers first, because they say that nothing below this line is
45
+ // anyone's text. An error, gating by the default rule and deliberately
46
+ // without `gates`: that field is coherence's (issue.ts) and archive does
47
+ // not read validate's findings, so claiming it here would be a promise
48
+ // about a gate that is not wired. What makes it an error rather than a
49
+ // warning is what the next merge does — `loam archive` rewrites the
50
+ // requirements section of this very file and takes whichever marker lines
51
+ // fall inside it, turning a conflict anyone can see into a document nobody
52
+ // can tell is wrong.
53
+ const conflict = documentConflictFinding(`${service}: spec.md`, service, specText);
54
+ if (conflict !== null)
55
+ findings.push(conflict);
56
+ // A living spec with no `### Requirement:` block at all is the baseline
57
+ // `loam adopt` scaffolds and nobody ever filled in. "requirements covered
58
+ // (0 requirements, all with scenarios)" is true of it and says the opposite
59
+ // of what a reader needs: every cross-axis check downstream — API
60
+ // governance, the Operations: spine, gherkin generation — is vacuous here,
61
+ // and the green tick is what let a whole fleet score `sourced` over empty
62
+ // files.
63
+ if (reqs.length === 0) {
64
+ findings.push({
65
+ severity: "warn",
66
+ code: "spec.no-requirements",
67
+ subject: service,
68
+ message: `${service}: spec.md holds no '### Requirement:' blocks — every requirement-driven check below is vacuous, and nothing here can go out of date because nothing is written down`,
69
+ });
70
+ }
71
+ else {
72
+ findings.push(coverageFinding(`${service}: requirements`, reqs));
73
+ }
74
+ // The other half of the coverage question, and an error for the same
75
+ // reason the first half is: the whole claim of coverage is that a
76
+ // requirement has a scenario something can RUN. A heading with no steps
77
+ // satisfies the count and tests nothing — cucumber runs it vacuously green
78
+ // and `loam verify --results` can never confirm it — so a fleet gate that
79
+ // called it covered was certifying the absence of a test.
80
+ findings.push(...steplessFindings(`${service}: requirements`, service, reqs));
81
+ findings.push(...assertionlessFindings(`${service}: requirements`, service, reqs));
82
+ findings.push(...examplesFindings(`${service}: requirements`, service, reqs));
83
+ findings.push(...duplicateRequirementFindings(reqs, `${service}: spec.md`, service));
84
+ findings.push(...requirementIdFindings(reqs, `${service}: spec.md`, service));
85
+ findings.push(...repeatedListLineFindings(reqs, `${service}: spec.md`, service));
86
+ }
87
+ else {
88
+ findings.push({
89
+ severity: "warn",
90
+ code: "service.no-spec",
91
+ message: `No living spec at ${paths.spec} — requirement coverage and API governance are unchecked`,
92
+ });
93
+ }
94
+ // The requirements that still govern anything. A REMOVED requirement is on
95
+ // its way out together with the operations it names: it makes no claim on the
96
+ // contract and governs nothing once the retiring feature archives — the same
97
+ // position coherence/coherence.ts takes on the delta side.
98
+ //
99
+ // Spelled once because it was spelled three times and dropped on the fourth:
100
+ // `governedOps` filtered, `spec-api.op-undefined` filtered, and the
101
+ // `api.covered` join did not — so an operation whose only requirement was
102
+ // REMOVED counted as governed, and the one operation about to be left without
103
+ // a requirement was the one the report called covered.
104
+ const livingReqs = reqs.filter((r) => r.kind !== "REMOVED");
105
+ // The architecture spec, read HERE with spec.md rather than with the rest of
106
+ // its own axis below, because the event links are a requirement-level join and
107
+ // BOTH requirement files carry them. Events are architecture as often as they
108
+ // are business — the outbox requirement in a service's arch.spec.md is the
109
+ // canonical place a `Publishes:` line belongs — so handing the event axis only
110
+ // spec.md would silently ignore the lines most authors write.
111
+ const archText = existsSync(paths.archSpec)
112
+ ? fleet === undefined
113
+ ? await readFile(paths.archSpec, "utf8")
114
+ : await fleet.readText(paths.archSpec)
115
+ : null;
116
+ const archReqs = archText === null
117
+ ? []
118
+ : fleet === undefined
119
+ ? parseRequirements(archText)
120
+ : await fleet.readRequirements(paths.archSpec);
121
+ return { findings, reqs, livingReqs, archText, archReqs };
122
+ }
123
+ /**
124
+ * The topology a `node:` Covers entry resolves against, loaded at most once and
125
+ * only when something asks for it.
126
+ *
127
+ * Under `--all` the caller hands in the doc it already loaded — the whole
128
+ * `architecture/` project — so the deployment model is already here and this
129
+ * returns it. A SINGLE-service run loads only `landscape.likec4`, and a fleet
130
+ * that keeps its `deployment { }` block in a file beside it, which is the
131
+ * ordinary layout once one landscape file is the fleet's convention, would then
132
+ * resolve one `Covers:` line two different ways depending on which command
133
+ * asked. `loam status`'s two forms had exactly that disagreement and it was a
134
+ * defect both times; this is the same class, prevented rather than found.
135
+ *
136
+ * The cost is paid only by a service whose `arch.spec.md` actually writes a
137
+ * `node:` entry. A fleet with no topology at all pays one project load per such
138
+ * service — a state in which the entry is a typo anyway, and one load is what
139
+ * buys it a message that names the real ids.
140
+ */
141
+ async function topology(land, archReqs, docsDir) {
142
+ if (land?.deployment !== undefined && hasDeployment(land.deployment))
143
+ return land.deployment;
144
+ const wanted = coversEntries(archReqs).some((e) => e.form === "node" || e.form === "node-edge");
145
+ if (!wanted)
146
+ return undefined;
147
+ const project = await loadArchitecture(docsDir);
148
+ return project.errors.length === 0 ? project.deployment : undefined;
149
+ }
150
+ export async function archAxisFindings(axis) {
151
+ const { service, paths, archText, archReqs, elements, relationships, land, known, docsDir } = axis;
152
+ const findings = [];
153
+ // The architecture spec axis — the obligations a business spec never carries
154
+ // (the transactional outbox, retries, metrics, alerts). arch.spec.md is
155
+ // optional and its ABSENCE is not a finding (partial adoption is supported);
156
+ // what exists must hold together: every requirement needs a scenario, every
157
+ // `Covers:` entry must resolve (covers.unknown), and every alert/SLI that
158
+ // health.yaml declares wants a covering requirement (health.uncovered) — the
159
+ // moment health.yaml stops being inert. Warnings, never gates: `--strict` is
160
+ // the CI escalation.
161
+ const health = await readHealth(paths.health);
162
+ // A model that reaches nothing is almost never true — but only when
163
+ // something else attests it should reach. A deliberately shallow synthetic
164
+ // fixture can hold elements without relationships beside sources that name
165
+ // dependencies, yet `0 errors, 0 warnings` would still read as done.
166
+ // Evidence-gated on purpose: a bare root-plus-one-container baseline stays
167
+ // silent (nothing PROVES it thin — the standard mid-adoption shape), the
168
+ // warn needs either a second nested element with no edge joining anything,
169
+ // or dependencies declared in this service's own health.yaml. The health
170
+ // half goes quiet when the file is unreadable — no claims on bad data.
171
+ const nested = elements.filter((e) => e.id.includes(".")).length;
172
+ const deps = health.unreadable ? [] : health.dependencies;
173
+ if (elements.length > 0 && relationships.length === 0 && (nested > 1 || deps.length > 0)) {
174
+ const evidence = [
175
+ ...(nested > 1 ? [`${nested} nested elements with no edge joining them`] : []),
176
+ ...(deps.length > 0
177
+ ? [`health.yaml declares ${deps.length} dependenc${deps.length === 1 ? "y" : "ies"} (${deps.join(", ")})`]
178
+ : []),
179
+ ].join(", and ");
180
+ findings.push({
181
+ severity: "warn",
182
+ code: "c4.no-relationships",
183
+ subject: service,
184
+ message: `${service}: model.likec4 declares ${elements.length} element(s) and 0 relationships, yet ${evidence} — ` +
185
+ `a model that reaches nothing is almost never true; draw the edges the service actually has ` +
186
+ `(what its containers call, what reads and writes its stores, what it depends on at runtime)`,
187
+ });
188
+ }
189
+ if (archText !== null) {
190
+ // The arch axis is advisory in what it ASKS for (covers.unknown,
191
+ // health.uncovered are warnings) but not in whether the file is readable:
192
+ // both breaches below are about the document itself, and they are graded
193
+ // exactly as they are on the business axis — one file, one rule.
194
+ const conflict = documentConflictFinding(`${service}: arch.spec.md`, service, archText);
195
+ if (conflict !== null)
196
+ findings.push(conflict);
197
+ findings.push(coverageFinding(`${service}: arch requirements`, archReqs));
198
+ findings.push(...steplessFindings(`${service}: arch requirements`, service, archReqs));
199
+ findings.push(...assertionlessFindings(`${service}: arch requirements`, service, archReqs));
200
+ findings.push(...examplesFindings(`${service}: arch requirements`, service, archReqs));
201
+ findings.push(...duplicateRequirementFindings(archReqs, `${service}: arch.spec.md`, service));
202
+ findings.push(...requirementIdFindings(archReqs, `${service}: arch.spec.md`, service));
203
+ findings.push(...repeatedListLineFindings(archReqs, `${service}: arch.spec.md`, service));
204
+ }
205
+ // A health.yaml that exists but does not read is reported once, and the
206
+ // checks that read it go quiet BOTH ways: its ids are unknown, so no
207
+ // health.uncovered obligation exists (the empty id set below is already
208
+ // silent), and no `Covers: alert:/sli:` entry may be graded a typo against
209
+ // ids nobody could read (coversUnknownFindings mutes on the flag). A warn,
210
+ // not an error: the axis it feeds is advisory end to end.
211
+ if (health.unreadable) {
212
+ findings.push({
213
+ severity: "warn",
214
+ code: "health.invalid",
215
+ subject: service,
216
+ message: `${service}: health.yaml does not parse — alert/SLI ids are unreadable, so Covers: alert:/sli: entries and health coverage are unchecked`,
217
+ ...(health.error === undefined ? {} : { details: [health.error] }),
218
+ });
219
+ }
220
+ const landParses = land !== null && land.errors.length === 0 ? land : null;
221
+ const deployment = await topology(landParses, archReqs, docsDir);
222
+ const scope = {
223
+ elements: [...elements, ...(landParses?.elements ?? [])],
224
+ relationships: [...relationships, ...(landParses?.relationships ?? [])],
225
+ ...(deployment === undefined ? {} : { deployment }),
226
+ health: health.ids,
227
+ known,
228
+ };
229
+ findings.push(...coversUnknownFindings(archReqs, { where: `${service}: arch.spec.md`, subject: service }, scope, health.unreadable));
230
+ const activeCovers = coversEntries(archReqs);
231
+ for (const { form, ids } of [
232
+ { form: "alert", ids: health.ids.alerts },
233
+ { form: "sli", ids: health.ids.slis },
234
+ ]) {
235
+ for (const id of ids) {
236
+ if (activeCovers.some((e) => e.form === form && e.id === id))
237
+ continue;
238
+ findings.push({
239
+ severity: "warn",
240
+ code: "health.uncovered",
241
+ subject: service,
242
+ message: `${service}: health.yaml declares ${form === "alert" ? "alert" : "SLI"} '${id}' but no arch.spec.md requirement covers it — write one with 'Covers: ${form}:${id}', or the signal ships with nothing testing it`,
243
+ });
244
+ }
245
+ }
246
+ // The other direction health.yaml is read in: its dependencies must be the
247
+ // model's dependencies. `deps` is already muted on an unreadable file above.
248
+ findings.push(...healthDependencyFindings({ service, dependencies: deps, elements, modelPath: paths.model }));
249
+ return findings;
250
+ }
251
+ //# sourceMappingURL=specs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs.js","sourceRoot":"","sources":["../../../../src/commands/validate/service/specs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * This service's requirement documents: spec.md, arch.spec.md, and the\n * obligations the second of them carries that the first never does.\n *\n * Two entry points because the two happen at different moments in a service's\n * grading and the order of findings is part of the report. `readServiceSpecs`\n * runs first — everything downstream joins through the requirements it parses —\n * while `archAxisFindings` runs last, after both spines, because `covers.unknown`\n * resolves against the C4 model and the landscape and cannot be answered until\n * those are in hand.\n *\n * The arch axis is advisory in what it ASKS for (covers.unknown,\n * health.uncovered are warnings; arch.spec.md's absence is not a finding at all,\n * because partial adoption is supported) but not in whether the file is\n * readable: a conflict marker or a stepless scenario is graded exactly as it is\n * on the business axis. One file, one rule.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { type Elem, type LoadedDoc, type Rel } from \"../../../core/c4/likec4.js\";\nimport { type PathableService } from \"../../../core/kernel/ids/service.js\";\nimport { type ServicePaths } from \"../../../core/repo/paths.js\";\nimport { type Finding } from \"../../../core/vocabulary/report.js\";\nimport { parseRequirements } from \"../../../core/document/parse.js\";\nimport {\n assertionlessFindings,\n examplesFindings,\n steplessFindings,\n} from \"../../../core/document/scenarios.js\";\nimport { type Requirement } from \"../../../core/document/spec.js\";\nimport { type CoverageScope } from \"../../../core/c4/arch.js\";\nimport { hasDeployment, type DeploymentModel } from \"../../../core/c4/parsed/deployment.js\";\nimport { loadArchitecture } from \"../../../core/c4/project/architecture.js\";\nimport { type DocsDir } from \"../../../core/kernel/ids/dirs.js\";\nimport { readHealth } from \"../../../core/vocabulary/health.js\";\nimport { documentConflictFinding } from \"../../../core/conflict-markers.js\";\nimport { FleetContext } from \"../../../core/fleet-context.js\";\nimport { healthDependencyFindings } from \"../checks/health-deps.js\";\nimport {\n coverageFinding,\n coversEntries,\n coversUnknownFindings,\n duplicateRequirementFindings,\n repeatedListLineFindings,\n requirementIdFindings,\n} from \"../checks/requirements.js\";\n\n/** Both requirement documents, parsed once for every axis that joins through them. */\nexport interface ServiceSpecs {\n /** spec.md's findings, emitted before any cross-axis check runs. */\n findings: Finding[];\n /** Every requirement spec.md declares, REMOVED ones included. */\n reqs: Requirement[];\n /** The ones that still govern something. */\n livingReqs: Requirement[];\n /** arch.spec.md's text, or null when the service has none. */\n archText: string | null;\n archReqs: Requirement[];\n}\n\nexport async function readServiceSpecs(input: {\n service: PathableService;\n paths: ServicePaths;\n fleet?: FleetContext;\n}): Promise<ServiceSpecs> {\n const { service, paths, fleet } = input;\n const findings: Finding[] = [];\n\n // Requirement coverage.\n let reqs: Requirement[] = [];\n if (existsSync(paths.spec)) {\n const specText =\n fleet === undefined ? await readFile(paths.spec, \"utf8\") : await fleet.readText(paths.spec);\n reqs = fleet === undefined ? parseRequirements(specText) : await fleet.readRequirements(paths.spec);\n // Conflict markers first, because they say that nothing below this line is\n // anyone's text. An error, gating by the default rule and deliberately\n // without `gates`: that field is coherence's (issue.ts) and archive does\n // not read validate's findings, so claiming it here would be a promise\n // about a gate that is not wired. What makes it an error rather than a\n // warning is what the next merge does — `loam archive` rewrites the\n // requirements section of this very file and takes whichever marker lines\n // fall inside it, turning a conflict anyone can see into a document nobody\n // can tell is wrong.\n const conflict = documentConflictFinding(`${service}: spec.md`, service, specText);\n if (conflict !== null) findings.push(conflict);\n // A living spec with no `### Requirement:` block at all is the baseline\n // `loam adopt` scaffolds and nobody ever filled in. \"requirements covered\n // (0 requirements, all with scenarios)\" is true of it and says the opposite\n // of what a reader needs: every cross-axis check downstream — API\n // governance, the Operations: spine, gherkin generation — is vacuous here,\n // and the green tick is what let a whole fleet score `sourced` over empty\n // files.\n if (reqs.length === 0) {\n findings.push({\n severity: \"warn\",\n code: \"spec.no-requirements\",\n subject: service,\n message: `${service}: spec.md holds no '### Requirement:' blocks — every requirement-driven check below is vacuous, and nothing here can go out of date because nothing is written down`,\n });\n } else {\n findings.push(coverageFinding(`${service}: requirements`, reqs));\n }\n // The other half of the coverage question, and an error for the same\n // reason the first half is: the whole claim of coverage is that a\n // requirement has a scenario something can RUN. A heading with no steps\n // satisfies the count and tests nothing — cucumber runs it vacuously green\n // and `loam verify --results` can never confirm it — so a fleet gate that\n // called it covered was certifying the absence of a test.\n findings.push(...steplessFindings(`${service}: requirements`, service, reqs));\n findings.push(...assertionlessFindings(`${service}: requirements`, service, reqs));\n findings.push(...examplesFindings(`${service}: requirements`, service, reqs));\n findings.push(...duplicateRequirementFindings(reqs, `${service}: spec.md`, service));\n findings.push(...requirementIdFindings(reqs, `${service}: spec.md`, service));\n findings.push(...repeatedListLineFindings(reqs, `${service}: spec.md`, service));\n } else {\n findings.push({\n severity: \"warn\",\n code: \"service.no-spec\",\n message: `No living spec at ${paths.spec} — requirement coverage and API governance are unchecked`,\n });\n }\n\n // The requirements that still govern anything. A REMOVED requirement is on\n // its way out together with the operations it names: it makes no claim on the\n // contract and governs nothing once the retiring feature archives — the same\n // position coherence/coherence.ts takes on the delta side.\n //\n // Spelled once because it was spelled three times and dropped on the fourth:\n // `governedOps` filtered, `spec-api.op-undefined` filtered, and the\n // `api.covered` join did not — so an operation whose only requirement was\n // REMOVED counted as governed, and the one operation about to be left without\n // a requirement was the one the report called covered.\n const livingReqs = reqs.filter((r) => r.kind !== \"REMOVED\");\n\n // The architecture spec, read HERE with spec.md rather than with the rest of\n // its own axis below, because the event links are a requirement-level join and\n // BOTH requirement files carry them. Events are architecture as often as they\n // are business — the outbox requirement in a service's arch.spec.md is the\n // canonical place a `Publishes:` line belongs — so handing the event axis only\n // spec.md would silently ignore the lines most authors write.\n const archText = existsSync(paths.archSpec)\n ? fleet === undefined\n ? await readFile(paths.archSpec, \"utf8\")\n : await fleet.readText(paths.archSpec)\n : null;\n const archReqs =\n archText === null\n ? []\n : fleet === undefined\n ? parseRequirements(archText)\n : await fleet.readRequirements(paths.archSpec);\n\n return { findings, reqs, livingReqs, archText, archReqs };\n}\n\n/** What the arch axis needs once the model and the landscape have been read. */\nexport interface ArchAxis {\n service: PathableService;\n paths: ServicePaths;\n /** arch.spec.md's text, or null — its absence is not a finding. */\n archText: string | null;\n archReqs: Requirement[];\n /** The service's own C4 model, empty when it has none. */\n elements: Elem[];\n relationships: Rel[];\n /** The living landscape, or null when the repo has none. */\n land: LoadedDoc | null;\n /** The docs root, for the one lazy load below — see `deployment` in the scope. */\n docsDir: DocsDir;\n /**\n * The enumerated fleet, for the Covers matcher's endpoint resolution — the\n * same set the caller's own spine resolution uses, so a `Covers:` line\n * naming a service matches an edge drawn into that service's container.\n */\n known?: ReadonlySet<string>;\n}\n\n/**\n * The topology a `node:` Covers entry resolves against, loaded at most once and\n * only when something asks for it.\n *\n * Under `--all` the caller hands in the doc it already loaded — the whole\n * `architecture/` project — so the deployment model is already here and this\n * returns it. A SINGLE-service run loads only `landscape.likec4`, and a fleet\n * that keeps its `deployment { }` block in a file beside it, which is the\n * ordinary layout once one landscape file is the fleet's convention, would then\n * resolve one `Covers:` line two different ways depending on which command\n * asked. `loam status`'s two forms had exactly that disagreement and it was a\n * defect both times; this is the same class, prevented rather than found.\n *\n * The cost is paid only by a service whose `arch.spec.md` actually writes a\n * `node:` entry. A fleet with no topology at all pays one project load per such\n * service — a state in which the entry is a typo anyway, and one load is what\n * buys it a message that names the real ids.\n */\nasync function topology(\n land: LoadedDoc | null,\n archReqs: Requirement[],\n docsDir: DocsDir,\n): Promise<DeploymentModel | undefined> {\n if (land?.deployment !== undefined && hasDeployment(land.deployment)) return land.deployment;\n const wanted = coversEntries(archReqs).some((e) => e.form === \"node\" || e.form === \"node-edge\");\n if (!wanted) return undefined;\n const project = await loadArchitecture(docsDir);\n return project.errors.length === 0 ? project.deployment : undefined;\n}\n\nexport async function archAxisFindings(axis: ArchAxis): Promise<Finding[]> {\n const { service, paths, archText, archReqs, elements, relationships, land, known, docsDir } = axis;\n const findings: Finding[] = [];\n\n // The architecture spec axis — the obligations a business spec never carries\n // (the transactional outbox, retries, metrics, alerts). arch.spec.md is\n // optional and its ABSENCE is not a finding (partial adoption is supported);\n // what exists must hold together: every requirement needs a scenario, every\n // `Covers:` entry must resolve (covers.unknown), and every alert/SLI that\n // health.yaml declares wants a covering requirement (health.uncovered) — the\n // moment health.yaml stops being inert. Warnings, never gates: `--strict` is\n // the CI escalation.\n const health = await readHealth(paths.health);\n\n // A model that reaches nothing is almost never true — but only when\n // something else attests it should reach. A deliberately shallow synthetic\n // fixture can hold elements without relationships beside sources that name\n // dependencies, yet `0 errors, 0 warnings` would still read as done.\n // Evidence-gated on purpose: a bare root-plus-one-container baseline stays\n // silent (nothing PROVES it thin — the standard mid-adoption shape), the\n // warn needs either a second nested element with no edge joining anything,\n // or dependencies declared in this service's own health.yaml. The health\n // half goes quiet when the file is unreadable — no claims on bad data.\n const nested = elements.filter((e) => e.id.includes(\".\")).length;\n const deps = health.unreadable ? [] : health.dependencies;\n if (elements.length > 0 && relationships.length === 0 && (nested > 1 || deps.length > 0)) {\n const evidence = [\n ...(nested > 1 ? [`${nested} nested elements with no edge joining them`] : []),\n ...(deps.length > 0\n ? [`health.yaml declares ${deps.length} dependenc${deps.length === 1 ? \"y\" : \"ies\"} (${deps.join(\", \")})`]\n : []),\n ].join(\", and \");\n findings.push({\n severity: \"warn\",\n code: \"c4.no-relationships\",\n subject: service,\n message:\n `${service}: model.likec4 declares ${elements.length} element(s) and 0 relationships, yet ${evidence} — ` +\n `a model that reaches nothing is almost never true; draw the edges the service actually has ` +\n `(what its containers call, what reads and writes its stores, what it depends on at runtime)`,\n });\n }\n\n if (archText !== null) {\n // The arch axis is advisory in what it ASKS for (covers.unknown,\n // health.uncovered are warnings) but not in whether the file is readable:\n // both breaches below are about the document itself, and they are graded\n // exactly as they are on the business axis — one file, one rule.\n const conflict = documentConflictFinding(`${service}: arch.spec.md`, service, archText);\n if (conflict !== null) findings.push(conflict);\n findings.push(coverageFinding(`${service}: arch requirements`, archReqs));\n findings.push(...steplessFindings(`${service}: arch requirements`, service, archReqs));\n findings.push(...assertionlessFindings(`${service}: arch requirements`, service, archReqs));\n findings.push(...examplesFindings(`${service}: arch requirements`, service, archReqs));\n findings.push(...duplicateRequirementFindings(archReqs, `${service}: arch.spec.md`, service));\n findings.push(...requirementIdFindings(archReqs, `${service}: arch.spec.md`, service));\n findings.push(...repeatedListLineFindings(archReqs, `${service}: arch.spec.md`, service));\n }\n // A health.yaml that exists but does not read is reported once, and the\n // checks that read it go quiet BOTH ways: its ids are unknown, so no\n // health.uncovered obligation exists (the empty id set below is already\n // silent), and no `Covers: alert:/sli:` entry may be graded a typo against\n // ids nobody could read (coversUnknownFindings mutes on the flag). A warn,\n // not an error: the axis it feeds is advisory end to end.\n if (health.unreadable) {\n findings.push({\n severity: \"warn\",\n code: \"health.invalid\",\n subject: service,\n message: `${service}: health.yaml does not parse — alert/SLI ids are unreadable, so Covers: alert:/sli: entries and health coverage are unchecked`,\n ...(health.error === undefined ? {} : { details: [health.error] }),\n });\n }\n const landParses = land !== null && land.errors.length === 0 ? land : null;\n const deployment = await topology(landParses, archReqs, docsDir);\n const scope: CoverageScope = {\n elements: [...elements, ...(landParses?.elements ?? [])],\n relationships: [...relationships, ...(landParses?.relationships ?? [])],\n ...(deployment === undefined ? {} : { deployment }),\n health: health.ids,\n known,\n };\n findings.push(\n ...coversUnknownFindings(archReqs, { where: `${service}: arch.spec.md`, subject: service }, scope, health.unreadable),\n );\n const activeCovers = coversEntries(archReqs);\n for (const { form, ids } of [\n { form: \"alert\" as const, ids: health.ids.alerts },\n { form: \"sli\" as const, ids: health.ids.slis },\n ]) {\n for (const id of ids) {\n if (activeCovers.some((e) => e.form === form && e.id === id)) continue;\n findings.push({\n severity: \"warn\",\n code: \"health.uncovered\",\n subject: service,\n message: `${service}: health.yaml declares ${form === \"alert\" ? \"alert\" : \"SLI\"} '${id}' but no arch.spec.md requirement covers it — write one with 'Covers: ${form}:${id}', or the signal ships with nothing testing it`,\n });\n }\n }\n // The other direction health.yaml is read in: its dependencies must be the\n // model's dependencies. `deps` is already muted on an unreadable file above.\n findings.push(...healthDependencyFindings({ service, dependencies: deps, elements, modelPath: paths.model }));\n\n return findings;\n}\n"]}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The inbound half of the C4↔API spine: every cross-system edge the living
3
+ * landscape draws INTO this service must resolve to a real operation in its
4
+ * OpenAPI.
5
+ *
6
+ * Separate from `./api.js` because the two grade different documents and the
7
+ * fix lives in different files — a broken edge is the fleet map's mistake, a
8
+ * missing operation is the contract's — but it may conclude nothing without
9
+ * `api.js`'s answer, which is why `ServiceContract` is a parameter rather than
10
+ * a second read of openapi.yaml.
11
+ *
12
+ * The event-side mirror of this check is inside `./events.js`: there the
13
+ * PRODUCER owns the contract and it lives in another repository, so the two
14
+ * could not share a walk even though they share the shape.
15
+ */
16
+ import {} from "../../../core/kernel/ids/service.js";
17
+ import {} from "../../../core/c4/likec4.js";
18
+ import {} from "../../../core/kernel/ids/service.js";
19
+ import {} from "../../../core/vocabulary/report.js";
20
+ import { errorText } from "../checks/vocabulary.js";
21
+ import {} from "./api.js";
22
+ export function spineFindings(spine) {
23
+ const { service, me, land, landSvcOf, contract } = spine;
24
+ const findings = [];
25
+ // Landscape spine: cross-system edges calling THIS service must resolve to a real
26
+ // operation in its OpenAPI — the C4↔API contract, checked in the living landscape,
27
+ // not only in feature mode. Catches dangling / de-linked op edges.
28
+ if (land !== null) {
29
+ if (land.errors.length > 0) {
30
+ // A living landscape that does not parse disables the C4↔API spine check —
31
+ // that is a broken source of truth, not a skippable detail.
32
+ //
33
+ // The parser's own output is attached ONCE per run, and not here when the
34
+ // run has a landscape target to carry it. One syntax error in one file
35
+ // becomes N copies of a dozen cascading diagnostics on a fleet of N
36
+ // services — the report stops being readable at exactly the moment
37
+ // somebody needs to read it, and the fix is one file either way.
38
+ findings.push({
39
+ severity: "error",
40
+ code: "spine.landscape-invalid",
41
+ subject: service,
42
+ message: `${service}: landscape.likec4 has ${land.errors.length} error(s) — spine check impossible` +
43
+ (spine.landscapeReported === true
44
+ ? "; the parser output is reported once, on the landscape target"
45
+ : ""),
46
+ ...(spine.landscapeReported === true ? {} : { details: land.errors.map(errorText) }),
47
+ });
48
+ }
49
+ else {
50
+ // Which element IS this service is the binding's call, then a title that
51
+ // names a real services/<id>/, and an edge into a modelled container
52
+ // counts as an edge into its service — matching the exact id alone meant a
53
+ // container edge left the spine without a word.
54
+ const svcOf = landSvcOf;
55
+ const opset = new Set(contract.ops);
56
+ let checked = 0;
57
+ let broken = 0;
58
+ for (const r of land.relationships) {
59
+ if (svcOf(r.target) !== me)
60
+ continue;
61
+ if (r.op !== undefined) {
62
+ // A contract that cannot be read — broken, or not there at all —
63
+ // proves nothing about this edge, neither broken nor resolved.
64
+ // Grading the edges against an empty operation set turned ONE root
65
+ // cause (the file) into one `spine.op-undefined` per inbound edge, so
66
+ // a service with twelve consumers reported twelve landscape defects
67
+ // and never named the file. `openapi.invalid` / `service.no-openapi`
68
+ // already did, from ./api.js; only op-link-missing (which never reads
69
+ // the contract) stays live, and `checked` stays 0 so no false
70
+ // spine.resolved is claimed either.
71
+ if (contract.unreadable)
72
+ continue;
73
+ checked += 1;
74
+ if (!opset.has(r.op)) {
75
+ broken += 1;
76
+ findings.push({
77
+ severity: "error",
78
+ code: "spine.op-undefined",
79
+ message: `${service}: landscape edge ${svcOf(r.source)} → ${service} calls '${r.op}', not defined in ${service}'s OpenAPI`,
80
+ });
81
+ }
82
+ else if (contract.deprecated.has(r.op)) {
83
+ // The contract holds — the op is defined — but it is marked
84
+ // `deprecated: true`: the consumer is standing on a contract being
85
+ // retired, and should be migrating off it. Warn per inbound edge;
86
+ // a deprecated op nobody calls raises no spine finding at all.
87
+ findings.push({
88
+ severity: "warn",
89
+ code: "spine.op-deprecated",
90
+ message: `${service}: landscape edge ${svcOf(r.source)} → ${service} calls '${r.op}', which ${service}'s OpenAPI marks deprecated — the consumer should migrate off it`,
91
+ });
92
+ }
93
+ }
94
+ else if ((r.title ?? "").toLowerCase().startsWith("call")) {
95
+ findings.push({
96
+ severity: "warn",
97
+ code: "spine.op-link-missing",
98
+ message: `${service}: landscape edge ${svcOf(r.source)} → ${service} ("${r.title}") has no operation link (metadata { op })`,
99
+ });
100
+ }
101
+ }
102
+ if (broken === 0 && checked > 0) {
103
+ findings.push({
104
+ severity: "ok",
105
+ code: "spine.resolved",
106
+ message: `${service}: landscape spine (${checked} inbound call(s) resolve to OpenAPI)`,
107
+ });
108
+ }
109
+ }
110
+ }
111
+ return findings;
112
+ }
113
+ //# sourceMappingURL=spine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spine.js","sourceRoot":"","sources":["../../../../src/commands/validate/service/spine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The inbound half of the C4↔API spine: every cross-system edge the living\n * landscape draws INTO this service must resolve to a real operation in its\n * OpenAPI.\n *\n * Separate from `./api.js` because the two grade different documents and the\n * fix lives in different files — a broken edge is the fleet map's mistake, a\n * missing operation is the contract's — but it may conclude nothing without\n * `api.js`'s answer, which is why `ServiceContract` is a parameter rather than\n * a second read of openapi.yaml.\n *\n * The event-side mirror of this check is inside `./events.js`: there the\n * PRODUCER owns the contract and it lives in another repository, so the two\n * could not share a walk even though they share the shape.\n */\nimport { type DeclaredService } from \"../../../core/kernel/ids/service.js\";\nimport { type LoadedDoc } from \"../../../core/c4/likec4.js\";\nimport { type PathableService } from \"../../../core/kernel/ids/service.js\";\nimport { type Finding } from \"../../../core/vocabulary/report.js\";\nimport { errorText } from \"../checks/vocabulary.js\";\nimport { type ServiceContract } from \"./api.js\";\n\n/** What the service target hands the spine check. */\nexport interface Spine {\n service: PathableService;\n /** `service` widened for `===` against the resolver's DOCUMENT text — see service.ts. */\n me: string;\n /** The living landscape, or null when the repo has none. */\n land: LoadedDoc | null;\n /** Its element→service resolver; null exactly when `land` is. */\n landSvcOf: ((id: string) => DeclaredService) | null;\n contract: ServiceContract;\n /** True when this run also emits a landscape target — see `ServiceCheck`. */\n landscapeReported?: boolean;\n}\n\nexport function spineFindings(spine: Spine): Finding[] {\n const { service, me, land, landSvcOf, contract } = spine;\n const findings: Finding[] = [];\n\n // Landscape spine: cross-system edges calling THIS service must resolve to a real\n // operation in its OpenAPI — the C4↔API contract, checked in the living landscape,\n // not only in feature mode. Catches dangling / de-linked op edges.\n if (land !== null) {\n if (land.errors.length > 0) {\n // A living landscape that does not parse disables the C4↔API spine check —\n // that is a broken source of truth, not a skippable detail.\n //\n // The parser's own output is attached ONCE per run, and not here when the\n // run has a landscape target to carry it. One syntax error in one file\n // becomes N copies of a dozen cascading diagnostics on a fleet of N\n // services — the report stops being readable at exactly the moment\n // somebody needs to read it, and the fix is one file either way.\n findings.push({\n severity: \"error\",\n code: \"spine.landscape-invalid\",\n subject: service,\n message:\n `${service}: landscape.likec4 has ${land.errors.length} error(s) — spine check impossible` +\n (spine.landscapeReported === true\n ? \"; the parser output is reported once, on the landscape target\"\n : \"\"),\n ...(spine.landscapeReported === true ? {} : { details: land.errors.map(errorText) }),\n });\n } else {\n // Which element IS this service is the binding's call, then a title that\n // names a real services/<id>/, and an edge into a modelled container\n // counts as an edge into its service — matching the exact id alone meant a\n // container edge left the spine without a word.\n const svcOf = landSvcOf!;\n const opset = new Set(contract.ops);\n let checked = 0;\n let broken = 0;\n for (const r of land.relationships) {\n if (svcOf(r.target) !== me) continue;\n if (r.op !== undefined) {\n // A contract that cannot be read — broken, or not there at all —\n // proves nothing about this edge, neither broken nor resolved.\n // Grading the edges against an empty operation set turned ONE root\n // cause (the file) into one `spine.op-undefined` per inbound edge, so\n // a service with twelve consumers reported twelve landscape defects\n // and never named the file. `openapi.invalid` / `service.no-openapi`\n // already did, from ./api.js; only op-link-missing (which never reads\n // the contract) stays live, and `checked` stays 0 so no false\n // spine.resolved is claimed either.\n if (contract.unreadable) continue;\n checked += 1;\n if (!opset.has(r.op)) {\n broken += 1;\n findings.push({\n severity: \"error\",\n code: \"spine.op-undefined\",\n message: `${service}: landscape edge ${svcOf(r.source)} → ${service} calls '${r.op}', not defined in ${service}'s OpenAPI`,\n });\n } else if (contract.deprecated.has(r.op)) {\n // The contract holds — the op is defined — but it is marked\n // `deprecated: true`: the consumer is standing on a contract being\n // retired, and should be migrating off it. Warn per inbound edge;\n // a deprecated op nobody calls raises no spine finding at all.\n findings.push({\n severity: \"warn\",\n code: \"spine.op-deprecated\",\n message: `${service}: landscape edge ${svcOf(r.source)} → ${service} calls '${r.op}', which ${service}'s OpenAPI marks deprecated — the consumer should migrate off it`,\n });\n }\n } else if ((r.title ?? \"\").toLowerCase().startsWith(\"call\")) {\n findings.push({\n severity: \"warn\",\n code: \"spine.op-link-missing\",\n message: `${service}: landscape edge ${svcOf(r.source)} → ${service} (\"${r.title}\") has no operation link (metadata { op })`,\n });\n }\n }\n if (broken === 0 && checked > 0) {\n findings.push({\n severity: \"ok\",\n code: \"spine.resolved\",\n message: `${service}: landscape spine (${checked} inbound call(s) resolve to OpenAPI)`,\n });\n }\n }\n }\n\n return findings;\n}\n"]}