@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,113 @@
1
+ /**
2
+ * Check 3 of `loam gate`: the verification records of every active feature
3
+ * touching the gated service, read through `verificationState`'s own graded
4
+ * verdicts — never a re-derivation of "verified" (that module's header is the
5
+ * contract). Split from `./checks.ts` when the four checks outgrew the line
6
+ * limit; this is the one check with a per-feature phase of its own, so it is
7
+ * the seam.
8
+ *
9
+ * Report, never fail: every finding here is a warning, by the item's own
10
+ * design — a deploy gate that hard-failed on unverified work would teach
11
+ * everyone that the cheapest way past it is an empty record.
12
+ */
13
+ import { inOrder } from "../kernel/concurrency.js";
14
+ import { verificationState } from "../status/verification.js";
15
+ import { listFeatures } from "../repo/repo.js";
16
+ import { featurePaths } from "../repo/paths.js";
17
+ import { compareIds } from "../repo/entries.js";
18
+ import { unreadableSubject } from "./report.js";
19
+ export async function verificationCheck(req) {
20
+ const active = await listFeatures(req.docsDir, {}, req.fleet);
21
+ // "Touching" = carries a specs/<svc>/ delta — the same definition
22
+ // `status --service` answers with. A feature whose C4 delta merely draws an
23
+ // edge into the service is deliberately out: counting it needs a delta parse
24
+ // per active feature, and it can be added additively if a fleet asks.
25
+ const touching = active
26
+ .filter((f) => f.services.includes(req.service))
27
+ .sort((a, b) => compareIds(a.id, b.id));
28
+ // COST ACCEPTED: each touching feature with a record derives its checklist,
29
+ // which parses its delta. That is the per-feature cost
30
+ // core/status/fleet/fleet.ts refuses for the WHOLE fleet; a deploy-time
31
+ // command scoped to one service pays it for that service's features only —
32
+ // with the invocation's context threaded through `verificationState` into
33
+ // the derivation, so its enumerations are memo hits (one fleet walk per
34
+ // run, not one per feature) and the batch prefetch below genuinely seeds
35
+ // the delta parses. The prefetch is an accelerator only: a host that cannot
36
+ // batch degrades to per-feature parses with identical findings.
37
+ await req.fleet.prefetchLikeC4(touching.filter((f) => f.has.delta).map((f) => featurePaths(f.dir).delta));
38
+ // inOrder caps the workspaces in flight and keeps the rows in feature order.
39
+ const rows = await inOrder(touching, async (f) => {
40
+ try {
41
+ const { state, notice } = await verificationState(req.docsDir, f, req.fleet);
42
+ return { kind: "read", feature: f, state, notice };
43
+ }
44
+ catch (err) {
45
+ return { kind: "unreadable", finding: unreadableSubject("feature", f.id, err) };
46
+ }
47
+ });
48
+ const features = [];
49
+ const findings = [];
50
+ for (const row of rows) {
51
+ if (row.kind === "unreadable") {
52
+ findings.push(row.finding);
53
+ continue;
54
+ }
55
+ features.push({ id: row.feature.id, ...row.state });
56
+ findings.push(...featureFindings(row, req.service));
57
+ }
58
+ return { features, findings };
59
+ }
60
+ function featureFindings(row, service) {
61
+ const { feature, state, notice } = row;
62
+ if (state.verdict === "verified")
63
+ return [];
64
+ if (state.verdict === "attested") {
65
+ // attestedNotice's own words — the one notice verify, list and status all
66
+ // show, converted to a Finding rather than re-derived. The code string
67
+ // (`verify.scenario-attested`) is spelled literally at its one emit site,
68
+ // core/verify/record.ts, where the drift guard collects it.
69
+ return notice === null
70
+ ? []
71
+ : [
72
+ {
73
+ severity: notice.severity,
74
+ code: notice.code,
75
+ subject: feature.id,
76
+ message: notice.message,
77
+ ...(notice.claims === undefined ? {} : { details: notice.claims }),
78
+ },
79
+ ];
80
+ }
81
+ const verify = `\`loam verify ${feature.id} --json\``;
82
+ const open = state.unconfirmed + state.unanswered;
83
+ const message = () => {
84
+ switch (state.state) {
85
+ case "absent":
86
+ return `${feature.id} touches '${service}' and has no verification record — nothing has been checked. Derive the checklist and answer it: ${verify}.`;
87
+ case "unreadable":
88
+ return `${feature.id}'s verification.yaml exists but does not read as a record — repair or delete it, then re-run ${verify}; nothing will overwrite it while it is unreadable.`;
89
+ case "stale":
90
+ return `${feature.id} moved after its record was written — the answers answer a checklist that is no longer the one being asked. Re-derive and re-answer: ${verify}.`;
91
+ case "recorded":
92
+ return state.claims === 0
93
+ ? `${feature.id}'s record answers no claims at all — nothing was asked, so nothing was checked. ${verify} re-derives the checklist.`
94
+ : `${open} of ${state.claims} claim(s) on ${feature.id} are open (${state.unconfirmed} unconfirmed, ${state.unanswered} unanswered) — close them with evidence: ${verify}.`;
95
+ default: {
96
+ // A fifth record state must land here as a build failure, not fall
97
+ // through into some rung nobody decided (verificationStatus holds the
98
+ // same guard for the same reason).
99
+ const unreachable = state.state;
100
+ throw new Error(`gate: no wording for verification state '${String(unreachable)}'`);
101
+ }
102
+ }
103
+ };
104
+ return [
105
+ {
106
+ severity: "warn",
107
+ code: "gate.feature-unverified",
108
+ subject: feature.id,
109
+ message: message(),
110
+ },
111
+ ];
112
+ }
113
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../../../src/core/gate/verification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AAOnE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,aAAa,CAAC;AAYlE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAA6B;IAE7B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9D,kEAAkE;IAClE,4EAA4E;IAC5E,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,QAAQ,GAAG,MAAM;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;SAC/C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,uDAAuD;IACvD,wEAAwE;IACxE,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,GAAG,CAAC,KAAK,CAAC,cAAc,CAC5B,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAC1E,CAAC;IACF,6EAA6E;IAC7E,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAA4B,EAAE;QACzE,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;YAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,iBAAiB,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QAClF,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,eAAe,CACtB,GAAqF,EACrF,OAAe;IAEf,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACvC,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,EAAE,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACjC,0EAA0E;QAC1E,uEAAuE;QACvE,0EAA0E;QAC1E,4DAA4D;QAC5D,OAAO,MAAM,KAAK,IAAI;YACpB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE;oBACE,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,OAAO,EAAE,OAAO,CAAC,EAAE;oBACnB,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;iBACnE;aACF,CAAC;IACR,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtD,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC;IAClD,MAAM,OAAO,GAAG,GAAW,EAAE;QAC3B,QAAQ,KAAK,CAAC,KAAK,EAAE,CAAC;YACpB,KAAK,QAAQ;gBACX,OAAO,GAAG,OAAO,CAAC,EAAE,aAAa,OAAO,oGAAoG,MAAM,GAAG,CAAC;YACxJ,KAAK,YAAY;gBACf,OAAO,GAAG,OAAO,CAAC,EAAE,gGAAgG,MAAM,qDAAqD,CAAC;YAClL,KAAK,OAAO;gBACV,OAAO,GAAG,OAAO,CAAC,EAAE,wIAAwI,MAAM,GAAG,CAAC;YACxK,KAAK,UAAU;gBACb,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;oBACvB,CAAC,CAAC,GAAG,OAAO,CAAC,EAAE,mFAAmF,MAAM,4BAA4B;oBACpI,CAAC,CAAC,GAAG,IAAI,OAAO,KAAK,CAAC,MAAM,gBAAgB,OAAO,CAAC,EAAE,cAAc,KAAK,CAAC,WAAW,iBAAiB,KAAK,CAAC,UAAU,4CAA4C,MAAM,GAAG,CAAC;YAChL,SAAS,CAAC;gBACR,mEAAmE;gBACnE,sEAAsE;gBACtE,mCAAmC;gBACnC,MAAM,WAAW,GAAU,KAAK,CAAC,KAAK,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,4CAA4C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACtF,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,OAAO;QACL;YACE,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,yBAAyB;YAC/B,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,OAAO,EAAE,OAAO,EAAE;SACnB;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Check 3 of `loam gate`: the verification records of every active feature\n * touching the gated service, read through `verificationState`'s own graded\n * verdicts — never a re-derivation of \"verified\" (that module's header is the\n * contract). Split from `./checks.ts` when the four checks outgrew the line\n * limit; this is the one check with a per-feature phase of its own, so it is\n * the seam.\n *\n * Report, never fail: every finding here is a warning, by the item's own\n * design — a deploy gate that hard-failed on unverified work would teach\n * everyone that the cheapest way past it is an empty record.\n */\nimport { inOrder } from \"../kernel/concurrency.js\";\nimport { verificationState } from \"../status/verification.js\";\nimport { listFeatures } from \"../repo/repo.js\";\nimport { featurePaths } from \"../repo/paths.js\";\nimport { compareIds, type FeatureEntry } from \"../repo/entries.js\";\nimport type { Finding } from \"../vocabulary/report.js\";\nimport type { VerificationState } from \"../status/report.js\";\nimport type { VerifyNotice } from \"../verify/record.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport type { RawServiceId } from \"../kernel/ids/service.js\";\nimport { unreadableSubject, type GateFeature } from \"./report.js\";\n\ntype VerificationRow =\n | { kind: \"read\"; feature: FeatureEntry; state: VerificationState; notice: VerifyNotice | null }\n | { kind: \"unreadable\"; finding: Finding };\n\nexport interface VerificationCheckRequest {\n docsDir: DocsDir;\n service: RawServiceId;\n fleet: FleetContext;\n}\n\nexport async function verificationCheck(\n req: VerificationCheckRequest,\n): Promise<{ features: GateFeature[]; findings: Finding[] }> {\n const active = await listFeatures(req.docsDir, {}, req.fleet);\n // \"Touching\" = carries a specs/<svc>/ delta — the same definition\n // `status --service` answers with. A feature whose C4 delta merely draws an\n // edge into the service is deliberately out: counting it needs a delta parse\n // per active feature, and it can be added additively if a fleet asks.\n const touching = active\n .filter((f) => f.services.includes(req.service))\n .sort((a, b) => compareIds(a.id, b.id));\n // COST ACCEPTED: each touching feature with a record derives its checklist,\n // which parses its delta. That is the per-feature cost\n // core/status/fleet/fleet.ts refuses for the WHOLE fleet; a deploy-time\n // command scoped to one service pays it for that service's features only —\n // with the invocation's context threaded through `verificationState` into\n // the derivation, so its enumerations are memo hits (one fleet walk per\n // run, not one per feature) and the batch prefetch below genuinely seeds\n // the delta parses. The prefetch is an accelerator only: a host that cannot\n // batch degrades to per-feature parses with identical findings.\n await req.fleet.prefetchLikeC4(\n touching.filter((f) => f.has.delta).map((f) => featurePaths(f.dir).delta),\n );\n // inOrder caps the workspaces in flight and keeps the rows in feature order.\n const rows = await inOrder(touching, async (f): Promise<VerificationRow> => {\n try {\n const { state, notice } = await verificationState(req.docsDir, f, req.fleet);\n return { kind: \"read\", feature: f, state, notice };\n } catch (err) {\n return { kind: \"unreadable\", finding: unreadableSubject(\"feature\", f.id, err) };\n }\n });\n const features: GateFeature[] = [];\n const findings: Finding[] = [];\n for (const row of rows) {\n if (row.kind === \"unreadable\") {\n findings.push(row.finding);\n continue;\n }\n features.push({ id: row.feature.id, ...row.state });\n findings.push(...featureFindings(row, req.service));\n }\n return { features, findings };\n}\n\nfunction featureFindings(\n row: { feature: FeatureEntry; state: VerificationState; notice: VerifyNotice | null },\n service: string,\n): Finding[] {\n const { feature, state, notice } = row;\n if (state.verdict === \"verified\") return [];\n if (state.verdict === \"attested\") {\n // attestedNotice's own words — the one notice verify, list and status all\n // show, converted to a Finding rather than re-derived. The code string\n // (`verify.scenario-attested`) is spelled literally at its one emit site,\n // core/verify/record.ts, where the drift guard collects it.\n return notice === null\n ? []\n : [\n {\n severity: notice.severity,\n code: notice.code,\n subject: feature.id,\n message: notice.message,\n ...(notice.claims === undefined ? {} : { details: notice.claims }),\n },\n ];\n }\n const verify = `\\`loam verify ${feature.id} --json\\``;\n const open = state.unconfirmed + state.unanswered;\n const message = (): string => {\n switch (state.state) {\n case \"absent\":\n return `${feature.id} touches '${service}' and has no verification record — nothing has been checked. Derive the checklist and answer it: ${verify}.`;\n case \"unreadable\":\n return `${feature.id}'s verification.yaml exists but does not read as a record — repair or delete it, then re-run ${verify}; nothing will overwrite it while it is unreadable.`;\n case \"stale\":\n return `${feature.id} moved after its record was written — the answers answer a checklist that is no longer the one being asked. Re-derive and re-answer: ${verify}.`;\n case \"recorded\":\n return state.claims === 0\n ? `${feature.id}'s record answers no claims at all — nothing was asked, so nothing was checked. ${verify} re-derives the checklist.`\n : `${open} of ${state.claims} claim(s) on ${feature.id} are open (${state.unconfirmed} unconfirmed, ${state.unanswered} unanswered) — close them with evidence: ${verify}.`;\n default: {\n // A fifth record state must land here as a build failure, not fall\n // through into some rung nobody decided (verificationStatus holds the\n // same guard for the same reason).\n const unreachable: never = state.state;\n throw new Error(`gate: no wording for verification state '${String(unreachable)}'`);\n }\n }\n };\n return [\n {\n severity: \"warn\",\n code: \"gate.feature-unverified\",\n subject: feature.id,\n message: message(),\n },\n ];\n}\n"]}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The scenario-identity digest — ONE recipe with three consumers, deliberately
3
+ * in one module: `verify/checklist.ts` folds it into `scenario.tested` claim
4
+ * ids and claim digests, and `./stamp.ts` stamps the same hex as the
5
+ * `@loam-digest-…` tag on every generated scenario, which is what a cucumber
6
+ * report carries and what `verify --results` matches on. A second spelling of
7
+ * the hash would silently stop every scenario claim from being answerable by a
8
+ * run — which is why this lives beside the stamper rather than inside either
9
+ * consumer.
10
+ */
11
+ import { createHash } from "node:crypto";
12
+ /**
13
+ * The full sha256 of a scenario's body — its lines joined and edge-trimmed,
14
+ * exactly as `serializeRequirements` frames them. The BODY, not the title,
15
+ * because rewriting the Given/When/Then under an unchanged heading is new text
16
+ * nobody answered for, and the promise — rewording a scenario renames its
17
+ * claim — has to hold for the words that actually specify the behaviour.
18
+ *
19
+ * `scenario.tested` claim ids fold in a prefix of this hex, a claim's `digest`
20
+ * and the `@loam-digest-<16hex>` tag `loam gherkin` stamps both take another
21
+ * (`verify/checklist.ts` owns those lengths, `./stamp.ts` its own) — so the
22
+ * claim, the stamp and the report `--results` reads can never disagree about
23
+ * what a scenario says.
24
+ *
25
+ * The identity is (SERVICE, AXIS, body), and both salts are there for the same
26
+ * reason: a digest that spans two namespaces lets one green run answer a
27
+ * question nobody ran a test for.
28
+ *
29
+ * - The AXIS salt keeps `spec.md` and `arch.spec.md` apart. Without it a green
30
+ * BUSINESS run answered the arch claim too — an integration test nobody wrote
31
+ * read as run.
32
+ * - The SERVICE salt keeps two repositories apart. Different services can word
33
+ * "the service returns 404 for an unknown id" identically; unsalted, those
34
+ * scenarios share one digest and one repository's green run can appear to
35
+ * confirm claims from suites that never ran each other's tests.
36
+ *
37
+ * The service salt is what makes that case CORRECT rather than merely refused:
38
+ * `contestedDigests` could only decline to answer a shared digest, which left a
39
+ * fleet with ordinary repeated wording holding claims nothing could ever
40
+ * answer.
41
+ *
42
+ * Both salts renamed every claim id and every stamped tag on the day they
43
+ * landed, and both readings are legible rather than silent: an existing
44
+ * `verification.yaml` answers a checklist digest that is no longer derived and
45
+ * reports STALE, and an existing generated `.feature` carries digests the
46
+ * living spec no longer computes and reports `gherkin.stale` beside the
47
+ * `gherkin.missing` for the new ones. One `loam gherkin` and one re-record
48
+ * clear both.
49
+ */
50
+ export function scenarioBodyHash(service, lines, axis = "business") {
51
+ // NUL-separated like claimId's tuples (verify/checklist.ts), and for its
52
+ // reason: no body can spell the salt in front of it, so the namespaces stay
53
+ // disjoint. Spelled as an escape and never as a raw NUL in the literal — a
54
+ // raw one makes this file read as `data` to `file(1)` and invisible to
55
+ // `grep`.
56
+ const body = lines.join("\n").trim();
57
+ const onAxis = axis === "arch" ? `arch.spec.md\u0000${body}` : body;
58
+ return createHash("sha256").update(`${service}\u0000${onAxis}`).digest("hex");
59
+ }
60
+ //# sourceMappingURL=digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.js","sourceRoot":"","sources":["../../../src/core/gherkin/digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,KAAe,EACf,IAAI,GAAiB,UAAU;IAE/B,yEAAyE;IACzE,4EAA4E;IAC5E,2EAA2E;IAC3E,uEAAuE;IACvE,UAAU;IACV,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,OAAO,SAAS,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC","sourcesContent":["/**\n * The scenario-identity digest — ONE recipe with three consumers, deliberately\n * in one module: `verify/checklist.ts` folds it into `scenario.tested` claim\n * ids and claim digests, and `./stamp.ts` stamps the same hex as the\n * `@loam-digest-…` tag on every generated scenario, which is what a cucumber\n * report carries and what `verify --results` matches on. A second spelling of\n * the hash would silently stop every scenario claim from being answerable by a\n * run — which is why this lives beside the stamper rather than inside either\n * consumer.\n */\nimport { createHash } from \"node:crypto\";\n\n/** The axis a scenario's words live on: `spec.md` or `arch.spec.md`. */\nexport type ScenarioAxis = \"business\" | \"arch\";\n\n/**\n * The full sha256 of a scenario's body — its lines joined and edge-trimmed,\n * exactly as `serializeRequirements` frames them. The BODY, not the title,\n * because rewriting the Given/When/Then under an unchanged heading is new text\n * nobody answered for, and the promise — rewording a scenario renames its\n * claim — has to hold for the words that actually specify the behaviour.\n *\n * `scenario.tested` claim ids fold in a prefix of this hex, a claim's `digest`\n * and the `@loam-digest-<16hex>` tag `loam gherkin` stamps both take another\n * (`verify/checklist.ts` owns those lengths, `./stamp.ts` its own) — so the\n * claim, the stamp and the report `--results` reads can never disagree about\n * what a scenario says.\n *\n * The identity is (SERVICE, AXIS, body), and both salts are there for the same\n * reason: a digest that spans two namespaces lets one green run answer a\n * question nobody ran a test for.\n *\n * - The AXIS salt keeps `spec.md` and `arch.spec.md` apart. Without it a green\n * BUSINESS run answered the arch claim too — an integration test nobody wrote\n * read as run.\n * - The SERVICE salt keeps two repositories apart. Different services can word\n * \"the service returns 404 for an unknown id\" identically; unsalted, those\n * scenarios share one digest and one repository's green run can appear to\n * confirm claims from suites that never ran each other's tests.\n *\n * The service salt is what makes that case CORRECT rather than merely refused:\n * `contestedDigests` could only decline to answer a shared digest, which left a\n * fleet with ordinary repeated wording holding claims nothing could ever\n * answer.\n *\n * Both salts renamed every claim id and every stamped tag on the day they\n * landed, and both readings are legible rather than silent: an existing\n * `verification.yaml` answers a checklist digest that is no longer derived and\n * reports STALE, and an existing generated `.feature` carries digests the\n * living spec no longer computes and reports `gherkin.stale` beside the\n * `gherkin.missing` for the new ones. One `loam gherkin` and one re-record\n * clear both.\n */\nexport function scenarioBodyHash(\n service: string,\n lines: string[],\n axis: ScenarioAxis = \"business\",\n): string {\n // NUL-separated like claimId's tuples (verify/checklist.ts), and for its\n // reason: no body can spell the salt in front of it, so the namespaces stay\n // disjoint. Spelled as an escape and never as a raw NUL in the literal — a\n // raw one makes this file read as `data` to `file(1)` and invisible to\n // `grep`.\n const body = lines.join(\"\\n\").trim();\n const onAxis = axis === \"arch\" ? `arch.spec.md\\u0000${body}` : body;\n return createHash(\"sha256\").update(`${service}\\u0000${onAxis}`).digest(\"hex\");\n}\n"]}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * A requirement set as a `.feature` file: what gets written where, and what the
3
+ * text says.
4
+ *
5
+ * The plan is separate from the render because the two answer to different
6
+ * owners — where a suite lands is the service repository's layout question
7
+ * (`./stamp.ts` owns the root), while what a scenario reads like is the
8
+ * requirement's. Every emitted scenario carries the digest tag the claim
9
+ * checklist matches on, which is why nothing here may reword a scenario body.
10
+ */
11
+ import {} from "../document/spec.js";
12
+ import { scenarioGherkin } from "../vocabulary/steps.js";
13
+ import {} from "../repo/paths.js";
14
+ import {} from "./digest.js";
15
+ import { gherkinStampLine, scenarioDigest, scenarioDigestTag } from "./stamp.js";
16
+ /** The axis label the machine contract speaks. */
17
+ export function axisLabel(axis) {
18
+ return axis.key === "archSpec" ? "arch" : "business";
19
+ }
20
+ /**
21
+ * A requirement name as a file name: lowercase, every non-letter/digit run one
22
+ * hyphen, edges trimmed.
23
+ *
24
+ * The keep-set is `\p{L}\p{N}`, not `[a-z0-9]`, because an ASCII-only slug is
25
+ * not a slug of a non-Latin fleet — it is the empty string. Every requirement
26
+ * written in Chinese, Japanese, Korean, Greek, Cyrillic or Hebrew slugged to
27
+ * nothing, fell back to `requirement`, and the whole service's suite came out
28
+ * as `requirement.feature`, `requirement-2.feature`, … : file names that name
29
+ * nothing, in the one place the mapping is supposed to be legible. So letters
30
+ * of every script survive as themselves.
31
+ *
32
+ * Accents are folded rather than kept, and that is the reason for NFKD first:
33
+ * decomposing splits `é` into `e` + a combining acute, and dropping the
34
+ * combining marks (`\p{M}`) leaves `e`. Without the fold `émojis` and `emojis`
35
+ * would be two files that look identical in a directory listing — and on a
36
+ * filesystem that normalises names (APFS, and HFS+ before it) they would be one
37
+ * file, silently, with the second emission overwriting the first.
38
+ *
39
+ * A name this still leaves empty (all punctuation or emoji) falls back to
40
+ * "requirement" and lets the collision counter number it. Runs collapse to a
41
+ * SINGLE hyphen, which is what keeps the `arch--` prefix an axis marker no
42
+ * business slug can spell.
43
+ */
44
+ export function slugOf(name) {
45
+ const s = name
46
+ .normalize("NFKD")
47
+ .replace(/\p{M}+/gu, "")
48
+ .toLowerCase()
49
+ .replace(/[^\p{L}\p{N}]+/gu, "-")
50
+ .replace(/^-+|-+$/g, "");
51
+ return s.length > 0 ? s : "requirement";
52
+ }
53
+ /**
54
+ * The emission plan for one scope: one `.feature` per requirement.
55
+ *
56
+ * File naming is the mapping's contract, so it is spelled here once:
57
+ * `<slug>.feature` for the business axis, `arch--<slug>.feature` for the
58
+ * architecture axis — flat and diffable, and collision-proof across axes
59
+ * because slugs collapse punctuation runs to a single hyphen, so no business
60
+ * slug can ever begin `arch--`. Two requirements slugging identically are
61
+ * disambiguated in document order: the first keeps the bare slug, later ones
62
+ * take `-2`, `-3`, … (counting up past any name that already owns the suffix).
63
+ *
64
+ * Tags ride the `Feature:` line and are inherited by every scenario in the
65
+ * file: `@<FEAT>` in feature mode (living emissions carry no feature tag),
66
+ * `@architecture` on the arch axis.
67
+ *
68
+ * `opts.service` is the suite's owner and salts every digest stamp — the same
69
+ * service `loam verify` files the matching claims under. Required, not
70
+ * defaulted: an emission that guessed it would stamp tags no claim matches.
71
+ */
72
+ export function planEmission(byAxis, opts) {
73
+ const used = new Set();
74
+ const out = [];
75
+ for (const { axis, reqs } of byAxis) {
76
+ for (const r of reqs) {
77
+ const base = (axis.key === "archSpec" ? "arch--" : "") + slugOf(r.name);
78
+ let fileName = `${base}.feature`;
79
+ for (let n = 2; used.has(fileName); n += 1)
80
+ fileName = `${base}-${n}.feature`;
81
+ used.add(fileName);
82
+ const tags = [
83
+ ...(opts.featureTag === undefined ? [] : [`@${opts.featureTag}`]),
84
+ ...(axis.key === "archSpec" ? ["@architecture"] : []),
85
+ ];
86
+ const { content, digests, stepless, malformedExamples, strandedBlocks } = renderFeature(r, { tags, version: opts.version }, opts.service, axisLabel(axis));
87
+ out.push({ fileName, axis, requirement: r, digests, stepless, malformedExamples, strandedBlocks, content });
88
+ }
89
+ }
90
+ return out;
91
+ }
92
+ export function renderFeature(r, stamp, service, axis = "business") {
93
+ const { tags, version } = stamp;
94
+ const lines = [gherkinStampLine(version)];
95
+ if (tags.length > 0)
96
+ lines.push(tags.join(" "));
97
+ lines.push(`Feature: ${r.name}`);
98
+ const text = r.text.join("\n").trim();
99
+ if (text.length > 0) {
100
+ lines.push("");
101
+ for (const l of text.split("\n"))
102
+ lines.push(l.length > 0 ? ` ${l}` : "");
103
+ }
104
+ const digests = [];
105
+ const stepless = [];
106
+ const malformedExamples = [];
107
+ const strandedBlocks = [];
108
+ for (const s of r.scenarios) {
109
+ // The digest hashes the WHOLE body, table rows included, so a changed cell
110
+ // is a changed scenario: `gherkin.stale` fires on an edited case exactly as
111
+ // it does on an edited step, and the claim it answers moves with it. It is
112
+ // also why a change to THIS renderer moves no digest: the hash is over the
113
+ // markdown source, so a suite generated before a grammar change keeps its
114
+ // stamps and grades `gherkin.current` until somebody regenerates it.
115
+ const digest = scenarioDigest(service, s.lines, axis);
116
+ digests.push(digest);
117
+ const { description, steps, examples, malformedExamples: broken, strandedBlocks: stranded } = scenarioGherkin(s.lines);
118
+ if (steps.length === 0)
119
+ stepless.push(s.name);
120
+ if (broken)
121
+ malformedExamples.push(s.name);
122
+ if (stranded)
123
+ strandedBlocks.push(s.name);
124
+ // The keyword is the whole difference between one case and twenty: cucumber
125
+ // expands an outline into one report element per row, each carrying this
126
+ // scenario's tag, and `runnerAnswers` confirms the claim only when every
127
+ // one of them passed.
128
+ lines.push("", ` ${scenarioDigestTag(digest)}`, ` ${examples === null ? "Scenario" : "Scenario Outline"}: ${s.name}`);
129
+ for (const d of description)
130
+ lines.push(` ${d}`);
131
+ for (const st of steps) {
132
+ lines.push(` ${st.text}`);
133
+ // Gherkin puts a step's argument UNDER the step, indented past it, and
134
+ // permits at most one of each kind. Docstring first, so a step carrying
135
+ // both reads request-then-expectation.
136
+ if (st.docstring !== undefined) {
137
+ lines.push(` """${st.docstring.contentType ?? ""}`);
138
+ // A payload containing its own `"""` would close the block early and
139
+ // silently move the rest of the request body into the step list.
140
+ for (const l of st.docstring.lines)
141
+ lines.push(l.length === 0 ? "" : ` ${l.replace(/"""/g, '\\"\\"\\"')}`);
142
+ lines.push(' """');
143
+ }
144
+ if (st.table !== undefined)
145
+ for (const row of st.table)
146
+ lines.push(` ${tableRow(row, st.table)}`);
147
+ }
148
+ if (examples !== null) {
149
+ lines.push("", " Examples:");
150
+ for (const row of [examples.header, ...examples.rows])
151
+ lines.push(` ${tableRow(row, [examples.header, ...examples.rows])}`);
152
+ }
153
+ }
154
+ return { content: lines.join("\n") + "\n", digests, stepless, malformedExamples, strandedBlocks };
155
+ }
156
+ /**
157
+ * One table row — an `Examples` case or a step's data table — with columns
158
+ * padded to the widest cell in that table.
159
+ *
160
+ * Padding is cosmetic to cucumber and deliberate here: these files are read in
161
+ * review as often as they are run, and an unaligned twenty-row matrix is where
162
+ * a wrong cell hides. Deterministic to the byte, like everything else the
163
+ * emitter writes — the widths come from the table alone.
164
+ *
165
+ * ESCAPING IS NOT COSMETIC. Gherkin's table grammar ends a cell at `|`, so a
166
+ * cell containing one silently becomes two cells and every row after it
167
+ * disagrees with the header; `\` and a literal newline are the same class. The
168
+ * escapes go on before the padding is measured, because the emitted width is
169
+ * the escaped width — measuring the raw cell produced columns that drifted by
170
+ * one character per escape.
171
+ */
172
+ function tableRow(row, all) {
173
+ const esc = (c) => c.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\n/g, "\\n");
174
+ const widths = row.map((_, i) => Math.max(...all.map((r) => esc(r[i] ?? "").length)));
175
+ return `| ${row.map((c, i) => esc(c).padEnd(widths[i])).join(" | ")} |`;
176
+ }
177
+ //# sourceMappingURL=emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.js","sourceRoot":"","sources":["../../../src/core/gherkin/emit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A requirement set as a `.feature` file: what gets written where, and what the\n * text says.\n *\n * The plan is separate from the render because the two answer to different\n * owners — where a suite lands is the service repository's layout question\n * (`./stamp.ts` owns the root), while what a scenario reads like is the\n * requirement's. Every emitted scenario carries the digest tag the claim\n * checklist matches on, which is why nothing here may reword a scenario body.\n */\nimport { type Requirement } from \"../document/spec.js\";\nimport { scenarioGherkin } from \"../vocabulary/steps.js\";\nimport { type SpecAxis } from \"../repo/paths.js\";\nimport { type ScenarioAxis } from \"./digest.js\";\nimport { gherkinStampLine, scenarioDigest, scenarioDigestTag } from \"./stamp.js\";\n\n/** The axis label the machine contract speaks. */\nexport function axisLabel(axis: SpecAxis): \"business\" | \"arch\" {\n return axis.key === \"archSpec\" ? \"arch\" : \"business\";\n}\n\n/**\n * A requirement name as a file name: lowercase, every non-letter/digit run one\n * hyphen, edges trimmed.\n *\n * The keep-set is `\\p{L}\\p{N}`, not `[a-z0-9]`, because an ASCII-only slug is\n * not a slug of a non-Latin fleet — it is the empty string. Every requirement\n * written in Chinese, Japanese, Korean, Greek, Cyrillic or Hebrew slugged to\n * nothing, fell back to `requirement`, and the whole service's suite came out\n * as `requirement.feature`, `requirement-2.feature`, … : file names that name\n * nothing, in the one place the mapping is supposed to be legible. So letters\n * of every script survive as themselves.\n *\n * Accents are folded rather than kept, and that is the reason for NFKD first:\n * decomposing splits `é` into `e` + a combining acute, and dropping the\n * combining marks (`\\p{M}`) leaves `e`. Without the fold `émojis` and `emojis`\n * would be two files that look identical in a directory listing — and on a\n * filesystem that normalises names (APFS, and HFS+ before it) they would be one\n * file, silently, with the second emission overwriting the first.\n *\n * A name this still leaves empty (all punctuation or emoji) falls back to\n * \"requirement\" and lets the collision counter number it. Runs collapse to a\n * SINGLE hyphen, which is what keeps the `arch--` prefix an axis marker no\n * business slug can spell.\n */\nexport function slugOf(name: string): string {\n const s = name\n .normalize(\"NFKD\")\n .replace(/\\p{M}+/gu, \"\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return s.length > 0 ? s : \"requirement\";\n}\n\nexport interface PlannedFeature {\n /** File name under `<gherkinDir>/loam/` — flat, never a subdirectory. */\n fileName: string;\n axis: SpecAxis;\n requirement: Requirement;\n /** One digest per scenario, in scenario order — what the stamps say. */\n digests: string[];\n /**\n * Names of scenarios that yielded ZERO steps — numbered-step or prose-only\n * legacy bodies. Their `Scenario:` blocks are description-only: cucumber\n * runs them vacuously green, while `--results` (which requires at least one\n * passed step) can never confirm them — so the emission must say so out\n * loud, and the fix is rewording the spec bullets, never editing the file.\n */\n stepless: string[];\n /**\n * Names of scenarios whose markdown table could not be read as `Examples` —\n * see `ScenarioGherkin.malformedExamples`. The table stays in the description\n * and the file is valid Gherkin, so nothing downstream can notice: the\n * emission is the only place this is visible at all.\n */\n malformedExamples: string[];\n /**\n * Names of scenarios that wrote a fenced block or an indented table where it\n * could not become a step argument — see `ScenarioGherkin.strandedBlocks`.\n * The text survives as description, so the file is valid Gherkin and the\n * document still reads correctly; what is gone is the ARGUMENT, and a step\n * definition that expected a payload or a table receives nothing.\n */\n strandedBlocks: string[];\n content: string;\n}\n\n/**\n * The emission plan for one scope: one `.feature` per requirement.\n *\n * File naming is the mapping's contract, so it is spelled here once:\n * `<slug>.feature` for the business axis, `arch--<slug>.feature` for the\n * architecture axis — flat and diffable, and collision-proof across axes\n * because slugs collapse punctuation runs to a single hyphen, so no business\n * slug can ever begin `arch--`. Two requirements slugging identically are\n * disambiguated in document order: the first keeps the bare slug, later ones\n * take `-2`, `-3`, … (counting up past any name that already owns the suffix).\n *\n * Tags ride the `Feature:` line and are inherited by every scenario in the\n * file: `@<FEAT>` in feature mode (living emissions carry no feature tag),\n * `@architecture` on the arch axis.\n *\n * `opts.service` is the suite's owner and salts every digest stamp — the same\n * service `loam verify` files the matching claims under. Required, not\n * defaulted: an emission that guessed it would stamp tags no claim matches.\n */\nexport function planEmission(\n byAxis: Array<{ axis: SpecAxis; reqs: Requirement[] }>,\n opts: { service: string; featureTag?: string; version: string },\n): PlannedFeature[] {\n const used = new Set<string>();\n const out: PlannedFeature[] = [];\n for (const { axis, reqs } of byAxis) {\n for (const r of reqs) {\n const base = (axis.key === \"archSpec\" ? \"arch--\" : \"\") + slugOf(r.name);\n let fileName = `${base}.feature`;\n for (let n = 2; used.has(fileName); n += 1) fileName = `${base}-${n}.feature`;\n used.add(fileName);\n const tags = [\n ...(opts.featureTag === undefined ? [] : [`@${opts.featureTag}`]),\n ...(axis.key === \"archSpec\" ? [\"@architecture\"] : []),\n ];\n const { content, digests, stepless, malformedExamples, strandedBlocks } = renderFeature(\n r,\n { tags, version: opts.version },\n opts.service,\n axisLabel(axis),\n );\n out.push({ fileName, axis, requirement: r, digests, stepless, malformedExamples, strandedBlocks, content });\n }\n }\n return out;\n}\n\n/**\n * One requirement as one `.feature` file: `Feature:` is the requirement name,\n * its body text (Operations:/Covers: lines included — they are part of what\n * the requirement says) becomes the feature description, and each scenario\n * renders under its `@loam-digest-…` tag — the tag line sits directly above\n * the `Scenario:` keyword, where Gherkin puts scenario-level tags, so the\n * runner attaches it to the scenario and carries it into the JSON report.\n * Deterministic to the byte.\n *\n * `service` is the owner of the suite being written, and it salts every digest\n * — the same service `loam verify` files the matching claims under. The two\n * have to be the same string or the stamps this writes match no claim at all.\n */\n/** What the emitted file is stamped with, beside the requirement it renders. */\nexport interface Stamp {\n tags: string[];\n version: string;\n}\n\nexport function renderFeature(\n r: Requirement,\n stamp: Stamp,\n service: string,\n axis: ScenarioAxis = \"business\",\n): { content: string; digests: string[]; stepless: string[]; malformedExamples: string[]; strandedBlocks: string[] } {\n const { tags, version } = stamp;\n const lines: string[] = [gherkinStampLine(version)];\n if (tags.length > 0) lines.push(tags.join(\" \"));\n lines.push(`Feature: ${r.name}`);\n const text = r.text.join(\"\\n\").trim();\n if (text.length > 0) {\n lines.push(\"\");\n for (const l of text.split(\"\\n\")) lines.push(l.length > 0 ? ` ${l}` : \"\");\n }\n const digests: string[] = [];\n const stepless: string[] = [];\n const malformedExamples: string[] = [];\n const strandedBlocks: string[] = [];\n for (const s of r.scenarios) {\n // The digest hashes the WHOLE body, table rows included, so a changed cell\n // is a changed scenario: `gherkin.stale` fires on an edited case exactly as\n // it does on an edited step, and the claim it answers moves with it. It is\n // also why a change to THIS renderer moves no digest: the hash is over the\n // markdown source, so a suite generated before a grammar change keeps its\n // stamps and grades `gherkin.current` until somebody regenerates it.\n const digest = scenarioDigest(service, s.lines, axis);\n digests.push(digest);\n const { description, steps, examples, malformedExamples: broken, strandedBlocks: stranded } = scenarioGherkin(s.lines);\n if (steps.length === 0) stepless.push(s.name);\n if (broken) malformedExamples.push(s.name);\n if (stranded) strandedBlocks.push(s.name);\n // The keyword is the whole difference between one case and twenty: cucumber\n // expands an outline into one report element per row, each carrying this\n // scenario's tag, and `runnerAnswers` confirms the claim only when every\n // one of them passed.\n lines.push(\"\", ` ${scenarioDigestTag(digest)}`, ` ${examples === null ? \"Scenario\" : \"Scenario Outline\"}: ${s.name}`);\n for (const d of description) lines.push(` ${d}`);\n for (const st of steps) {\n lines.push(` ${st.text}`);\n // Gherkin puts a step's argument UNDER the step, indented past it, and\n // permits at most one of each kind. Docstring first, so a step carrying\n // both reads request-then-expectation.\n if (st.docstring !== undefined) {\n lines.push(` \"\"\"${st.docstring.contentType ?? \"\"}`);\n // A payload containing its own `\"\"\"` would close the block early and\n // silently move the rest of the request body into the step list.\n for (const l of st.docstring.lines) lines.push(l.length === 0 ? \"\" : ` ${l.replace(/\"\"\"/g, '\\\\\"\\\\\"\\\\\"')}`);\n lines.push(' \"\"\"');\n }\n if (st.table !== undefined) for (const row of st.table) lines.push(` ${tableRow(row, st.table)}`);\n }\n if (examples !== null) {\n lines.push(\"\", \" Examples:\");\n for (const row of [examples.header, ...examples.rows]) lines.push(` ${tableRow(row, [examples.header, ...examples.rows])}`);\n }\n }\n return { content: lines.join(\"\\n\") + \"\\n\", digests, stepless, malformedExamples, strandedBlocks };\n}\n\n/**\n * One table row — an `Examples` case or a step's data table — with columns\n * padded to the widest cell in that table.\n *\n * Padding is cosmetic to cucumber and deliberate here: these files are read in\n * review as often as they are run, and an unaligned twenty-row matrix is where\n * a wrong cell hides. Deterministic to the byte, like everything else the\n * emitter writes — the widths come from the table alone.\n *\n * ESCAPING IS NOT COSMETIC. Gherkin's table grammar ends a cell at `|`, so a\n * cell containing one silently becomes two cells and every row after it\n * disagrees with the header; `\\` and a literal newline are the same class. The\n * escapes go on before the padding is measured, because the emitted width is\n * the escaped width — measuring the raw cell produced columns that drifted by\n * one character per escape.\n */\nfunction tableRow(row: string[], all: readonly string[][]): string {\n const esc = (c: string): string => c.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"\\\\n\");\n const widths = row.map((_, i) => Math.max(...all.map((r) => esc(r[i] ?? \"\").length)));\n return `| ${row.map((c, i) => esc(c).padEnd(widths[i]!)).join(\" | \")} |`;\n}\n"]}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * A generated `.feature` file read back — the stamp, the tags, the scenarios.
3
+ *
4
+ * The parser is deliberately shallow and forgiving in one direction only: a file
5
+ * without loam's stamp is not loam's to grade, and returns null rather than a
6
+ * partially understood suite. Anything that IS stamped is loam's output, so the
7
+ * digests inside it are trusted to be the ones `./emit.ts` wrote.
8
+ */
9
+ import { DIGEST_TAG_RE, STAMP_RE } from "./stamp.js";
10
+ /**
11
+ * Parse a `.feature` file for its loam stamps. Null when line 1 is not the
12
+ * stamp — an unstamped file is not loam's writing, and no staleness judgment
13
+ * may be built on it. Tag lines above `Feature:` are the file's tags; a tag
14
+ * line after it that carries a `@loam-digest-…` token binds that digest to the
15
+ * immediately following `Scenario:` line (tag lines stack, as Gherkin's do,
16
+ * without breaking the bond); any other non-blank line between them breaks it.
17
+ *
18
+ * DOCSTRING BODIES ARE SKIPPED WHOLE, and that is a correctness requirement
19
+ * rather than tidiness. Since a step may carry a `"""` payload, a request body
20
+ * containing a line that reads `Scenario: …` or begins with `@` would otherwise
21
+ * be read here as a real scenario or a real tag — and every staleness verdict
22
+ * (`gherkin.stale`, `gherkin.missing`, `gherkin.orphaned`) plus the tag that
23
+ * decides whether an in-flight feature's file may be replaced rests on this
24
+ * parse. The payload would be quietly deciding what the suite promises.
25
+ */
26
+ export function parseStampedFeature(text) {
27
+ const lines = text.split(/\r?\n/);
28
+ const m = STAMP_RE.exec(lines[0] ?? "");
29
+ if (!m)
30
+ return null;
31
+ const tags = [];
32
+ let featureName = null;
33
+ const scenarios = [];
34
+ let pending = null;
35
+ let inDocString = false;
36
+ for (const line of lines.slice(1)) {
37
+ // `"""` opens and closes; nothing between the two is structure. The emitter
38
+ // escapes an inner `"""` as `\"\"\"`, so an unescaped one here is always a
39
+ // real delimiter.
40
+ if (/^\s*"""/.test(line)) {
41
+ inDocString = !inDocString;
42
+ continue;
43
+ }
44
+ if (inDocString)
45
+ continue;
46
+ if (/^\s*@/.test(line)) {
47
+ for (const t of line.trim().split(/\s+/)) {
48
+ if (featureName === null) {
49
+ if (t.startsWith("@"))
50
+ tags.push(t.slice(1));
51
+ continue;
52
+ }
53
+ const d = DIGEST_TAG_RE.exec(t);
54
+ if (d)
55
+ pending = d[1];
56
+ }
57
+ continue;
58
+ }
59
+ const f = /^\s*Feature:\s*(.*)$/.exec(line);
60
+ if (f && featureName === null) {
61
+ featureName = f[1].trim();
62
+ continue;
63
+ }
64
+ const s = /^\s*Scenario(?: Outline)?:\s*(.*)$/.exec(line);
65
+ if (s) {
66
+ if (pending !== null)
67
+ scenarios.push({ name: s[1].trim(), digest: pending });
68
+ pending = null;
69
+ continue;
70
+ }
71
+ if (pending !== null && line.trim().length > 0)
72
+ pending = null;
73
+ }
74
+ return { version: m[1], tags, featureName, scenarios };
75
+ }
76
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/gherkin/read.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmBrD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClC,4EAA4E;QAC5E,2EAA2E;QAC3E,kBAAkB;QAClB,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,WAAW,GAAG,CAAC,WAAW,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,IAAI,WAAW;YAAE,SAAS;QAC1B,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;oBACzB,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;wBAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC7C,SAAS;gBACX,CAAC;gBACD,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChC,IAAI,CAAC;oBAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;YACzB,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,WAAW,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,EAAE,CAAC;YACN,IAAI,OAAO,KAAK,IAAI;gBAAE,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9E,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,IAAI,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AAC1D,CAAC","sourcesContent":["/**\n * A generated `.feature` file read back — the stamp, the tags, the scenarios.\n *\n * The parser is deliberately shallow and forgiving in one direction only: a file\n * without loam's stamp is not loam's to grade, and returns null rather than a\n * partially understood suite. Anything that IS stamped is loam's output, so the\n * digests inside it are trusted to be the ones `./emit.ts` wrote.\n */\nimport { DIGEST_TAG_RE, STAMP_RE } from \"./stamp.js\";\n\nexport interface StampedScenario {\n name: string;\n digest: string;\n}\n\n/** A generated file as the staleness checks see it. */\nexport interface StampedFeature {\n /** The version in the stamp line — which loam wrote the file. */\n version: string;\n /** Tag tokens (without `@`) from tag lines above `Feature:`. */\n tags: string[];\n /** The `Feature:` line's text — the requirement name, by the mapping. */\n featureName: string | null;\n /** Scenarios carrying a `@loam-digest-…` tag. A hand-added scenario has none and is invisible here. */\n scenarios: StampedScenario[];\n}\n\n/**\n * Parse a `.feature` file for its loam stamps. Null when line 1 is not the\n * stamp — an unstamped file is not loam's writing, and no staleness judgment\n * may be built on it. Tag lines above `Feature:` are the file's tags; a tag\n * line after it that carries a `@loam-digest-…` token binds that digest to the\n * immediately following `Scenario:` line (tag lines stack, as Gherkin's do,\n * without breaking the bond); any other non-blank line between them breaks it.\n *\n * DOCSTRING BODIES ARE SKIPPED WHOLE, and that is a correctness requirement\n * rather than tidiness. Since a step may carry a `\"\"\"` payload, a request body\n * containing a line that reads `Scenario: …` or begins with `@` would otherwise\n * be read here as a real scenario or a real tag — and every staleness verdict\n * (`gherkin.stale`, `gherkin.missing`, `gherkin.orphaned`) plus the tag that\n * decides whether an in-flight feature's file may be replaced rests on this\n * parse. The payload would be quietly deciding what the suite promises.\n */\nexport function parseStampedFeature(text: string): StampedFeature | null {\n const lines = text.split(/\\r?\\n/);\n const m = STAMP_RE.exec(lines[0] ?? \"\");\n if (!m) return null;\n const tags: string[] = [];\n let featureName: string | null = null;\n const scenarios: StampedScenario[] = [];\n let pending: string | null = null;\n let inDocString = false;\n for (const line of lines.slice(1)) {\n // `\"\"\"` opens and closes; nothing between the two is structure. The emitter\n // escapes an inner `\"\"\"` as `\\\"\\\"\\\"`, so an unescaped one here is always a\n // real delimiter.\n if (/^\\s*\"\"\"/.test(line)) {\n inDocString = !inDocString;\n continue;\n }\n if (inDocString) continue;\n if (/^\\s*@/.test(line)) {\n for (const t of line.trim().split(/\\s+/)) {\n if (featureName === null) {\n if (t.startsWith(\"@\")) tags.push(t.slice(1));\n continue;\n }\n const d = DIGEST_TAG_RE.exec(t);\n if (d) pending = d[1]!;\n }\n continue;\n }\n const f = /^\\s*Feature:\\s*(.*)$/.exec(line);\n if (f && featureName === null) {\n featureName = f[1]!.trim();\n continue;\n }\n const s = /^\\s*Scenario(?: Outline)?:\\s*(.*)$/.exec(line);\n if (s) {\n if (pending !== null) scenarios.push({ name: s[1]!.trim(), digest: pending });\n pending = null;\n continue;\n }\n if (pending !== null && line.trim().length > 0) pending = null;\n }\n return { version: m[1]!, tags, featureName, scenarios };\n}\n"]}